Start remote storage work. (Unbuild. Untested.)

This commit is contained in:
J-D-K 2025-03-06 13:19:01 -05:00
parent 11672043c3
commit 6b2283d45b
53 changed files with 1566 additions and 277 deletions

@ -1 +1 @@
Subproject commit 77517f2d5f0473113e63795b45f134b2a262f893
Subproject commit 1b823061b5a850fc384988443b912005ee9941a2

@ -1 +1 @@
Subproject commit fef8b0927d92a97af47f9e554d4fe310c5de66d8
Subproject commit 6fcae125af5734a996d115070d1595f4b5ad0562

View File

@ -32,7 +32,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
TARGET := JKSV
BUILD := build
SOURCES := source source/appstates source/ui source/data source/system source/fs
SOURCES := source source/appstates source/ui source/data source/system source/fs source/remote
DATA := data
INCLUDES := include ./Libraries/FsLib/Switch/FsLib/include ./Libraries/SDLLib/SDL/include
EXEFS_SRC := exefs_src

View File

@ -8,7 +8,7 @@
#include "sdl.hpp"
#include "strings.hpp"
#include "system/Task.hpp"
#include "ui/renderFunctions.hpp"
#include "ui/render_functions.hpp"
#include <memory>
#include <string>
#include <switch.h>
@ -44,8 +44,8 @@ class ConfirmState : public AppState
m_dataStruct(dataStruct)
{
// This is to make centering the Yes [A] string more accurate.
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::getWidth(22, m_yesString.c_str()) / 2);
m_noX = 820 - (sdl::text::getWidth(22, strings::get_by_name(strings::names::YES_NO, 1)) / 2);
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::get_width(22, m_yesString.c_str()) / 2);
m_noX = 820 - (sdl::text::get_width(22, strings::get_by_name(strings::names::YES_NO, 1)) / 2);
}
/// @brief Required even if it does nothing.
@ -84,18 +84,18 @@ class ConfirmState : public AppState
else if (TickCount >= 2000)
{
m_yesString = strings::get_by_name(strings::names::HOLDING_STRINGS, 2);
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::getWidth(22, m_yesString.c_str()) / 2);
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::get_width(22, m_yesString.c_str()) / 2);
}
else if (TickCount >= 1000)
{
m_yesString = strings::get_by_name(strings::names::HOLDING_STRINGS, 1);
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::getWidth(22, m_yesString.c_str()) / 2);
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::get_width(22, m_yesString.c_str()) / 2);
}
}
else if (input::button_released(HidNpadButton_A))
{
m_yesString = strings::get_by_name(strings::names::YES_NO, 0);
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::getWidth(22, m_yesString.c_str()) / 2);
m_yesX = YES_X_CENTER_COORDINATE - (sdl::text::get_width(22, m_yesString.c_str()) / 2);
}
else if (input::button_pressed(HidNpadButton_B))
{
@ -108,14 +108,14 @@ class ConfirmState : public AppState
void render(void)
{
// Dim background
sdl::renderRectFill(NULL, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
sdl::render_rect_fill(NULL, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
// Render dialog
ui::render_dialog_box(NULL, 280, 262, 720, 256);
// Text
sdl::text::render(NULL, 312, 288, 18, 656, colors::WHITE, m_queryString.c_str());
// Fake buttons. Maybe real later.
sdl::renderLine(NULL, 280, 454, 999, 454, colors::WHITE);
sdl::renderLine(NULL, 640, 454, 640, 517, colors::WHITE);
sdl::render_line(NULL, 280, 454, 999, 454, colors::WHITE);
sdl::render_line(NULL, 640, 454, 640, 517, colors::WHITE);
// To do: Position this better. Currently brought over from old code.
sdl::text::render(NULL, m_yesX, 476, 22, sdl::text::NO_TEXT_WRAP, colors::WHITE, m_yesString.c_str());
sdl::text::render(NULL,

94
include/curl/curl.hpp Normal file
View File

@ -0,0 +1,94 @@
#pragma once
#include "fslib.hpp"
#include <condition_variable>
#include <curl/curl.h>
#include <memory>
#include <mutex>
#include <vector>
namespace curl
{
/// @brief Universal user agent string used by JKSV for everything.
static constexpr std::string_view USER_AGENT_STRING = "JKSV";
/// @brief Used to store headers when needed.
using HeaderArray = std::vector<std::string>;
/// @brief Download buffer declaration.
using DownloadBuffer = std::vector<unsigned char>;
/// @brief This is the struct used for threaded downloads from drive.
typedef struct
{
/// @brief Conditional used for mutex.
std::condition_variable m_condition;
/// @brief Buffer mutex.
std::mutex m_bufferLock;
/// @brief Buffer both threads share.
std::unique_ptr<unsigned char[]> m_sharedBuffer;
/// @brief Path the to file to write to.
fslib::Path m_filePath;
/// @brief Size of the file being downloaded.
uint64_t m_fileSize;
/// @brief Current offset of the downloaded file.
uint64_t m_offset;
} DownloadStruct;
/// @brief This makes some stuff slightly easier to read. Also, I don't need to type as much.
using SharedDownloadStruct = std::shared_ptr<DownloadStruct>;
/// @brief Reads data from the target file to upload it.
/// @param buffer Incoming buffer from curl.
/// @param size Element size.
/// @param count Element count.
/// @param target Target file to read from.
/// @return Number of bytes read.
size_t read_from_file(char *buffer, size_t size, size_t count, fslib::File *target);
/// @brief Curl callback function that stores headers to a vector (curl::HeaderArray)
/// @param buffer Incoming buffer from curl.
/// @param size Size of the elements.
/// @param count Element count.
/// @param headerArray Vector to write to.
/// @return Number of bytes in the buffer.
size_t write_headers_array(const char *buffer, size_t size, size_t count, curl::HeaderArray *headerArray);
/// @brief Curl callback function to write the response to a C++ string.
/// @param buffer Buffer from curl
/// @param size Element size.
/// @param count Element count.
/// @param string String to write to.
/// @return Size written to string.
size_t write_response_string(const char *buffer, size_t size, size_t count, std::string *string);
/// @brief Writes the response to a download buffer (std::vector<unsigned char>).
/// @param buffer Buffer from curl.
/// @param size Element size.
/// @param count Element count.
/// @param bufferOut Buffer to write data to.
/// @return Size written to buffer.
size_t write_response_buffer(const char *buffer, size_t size, size_t count, curl::DownloadBuffer *bufferOut);
/// @brief Extracts the value of a header from the vector passed.
/// @param headerArray Vector of headers to search for the value in.
/// @param header Header to find and get the value of.
/// @param valueOut String to write the value to.
/// @return True if the value was found. False if it wasn't.
bool extract_value_from_headers(const curl::HeaderArray &headerArray,
std::string_view header,
std::string &valueOut);
/// @brief Quick shortcut function for a get request that writes the response to a C++ string.
/// @param url Url to GET.
/// @param stringOut String to write response to.
/// @return True on success. False on failure.
bool download_to_string(std::string_view url, std::string &stringOut);
/// @brief Quick shortcut function to get and download something to a buffer.
/// @param url URL to get and download.
/// @param buffer Buffer to write response to.
/// @return True on success. False on failure.
bool download_to_buffer(std::string_view url, curl::DownloadBuffer &bufferOut);
} // namespace curl

View File

@ -1,7 +1,7 @@
#pragma once
#include "fs/SaveMetaData.hpp"
#include "fs/directoryFunctions.hpp"
#include "fs/directory_functions.hpp"
#include "fs/io.hpp"
#include "fs/saveDataFunctions.hpp"
#include "fs/saveMount.hpp"
#include "fs/save_data_functions.hpp"
#include "fs/save_mount.hpp"
#include "fs/zip.hpp"

View File

@ -26,6 +26,10 @@ namespace fs
std::string_view commitDevice,
sys::ProgressTask *Task = nullptr);
/// @brief Writes data to infoOut to help clean up somes stuff.
/// @return Filled, complete zip_fileinfo struct.
zip_fileinfo create_zip_fileinfo(void);
/// @brief Returns whether or not zip has files inside.
/// @param zipPath Path to zip to check.
/// @return True if at least one file is found. False if none.

View File

@ -0,0 +1,101 @@
#pragma once
#include "Storage.hpp"
namespace remote
{
class GoogleDrive final : public remote::Storage
{
public:
/// @brief Attempts to initialize a new instance of the GoogleDrive class.
/// @param jsonPath Path to the client_secret file to read the information from.
GoogleDrive(std::string_view jsonPath);
/// @brief Destructor.
~GoogleDrive();
/// @brief Creates a new directory on Google Drive.
/// @param directoryName Name of the directory.
/// @param parentId Parent of the directory.
/// @return True on success. False on failure.
bool create_directory(std::string_view directoryName, std::string_view parentId);
/// @brief Uploads a file to Google drive.
/// @param filename The name of the file.
/// @param parentId The parent ID of the directory being uploaded to.
/// @param target File to upload.
/// @return True on success. False on failure.
bool upload_file(std::string_view filename, std::string_view parentId, fslib::File &target);
/// @brief Patches and updates a file on Google drive.
/// @param fileId The ID of the file to patch.
/// @param target File to patch/update from.
/// @return True on success. False on failure.
bool patch_file(std::string_view fileId, fslib::File &target);
/// @brief Downloads a file from Google Drive.
/// @param fileId ID of the file to download.
/// @param target File to write downloaded to.
/// @return True on success. False on failure.
bool download_file(std::string_view fileId, fslib::File &target);
/// @brief Deletes a file from Google Drive.
/// @param fileId ID of the file to delete.
/// @return True on success. False on failure.
bool delete_file(std::string_view fileId);
private:
/// @brief Client ID.
std::string m_clientId;
/// @brief Client secret ID.
std::string m_clientSecret;
/// @brief Token.
std::string m_token;
/// @brief Refresh token.
std::string m_refreshToken;
/// @brief This holds the authorization bearer header.
char m_authHeader[0x300] = {0};
/// @brief These are the headers used.
curl_slist *m_headers = nullptr;
/// @brief Allows the user to sign in to Google Drive using the Switch's internal browser.
/// @param authCodeOut String to write the authentication code to.
/// @return True on success. False on failure.
bool sign_in_and_authenticate(std::string &authCodeOut);
/// @brief Exchanges the authentication code with the server.
/// @param authCode Code to exchange.
/// @return True on success. False on failure.
bool exchange_auth_code(std::string_view authCode);
/// @brief Refreshes the token.
/// @return True on success. False on failure.
bool refresh_token(void);
/// @brief Checks if the token being used is still valid.
/// @return True if token is still valid. False if it isn't.
bool token_is_valid(void);
/// @brief Uses listingUrl to request a listing.
/// @param listingUrl URL to send the request to.
/// @param jsonOut String to write the received data to.
/// @return True on success. False on failure.
bool request_listing(const char *listingUrl, std::string &jsonOut);
/// @brief Processes the json of a listing request response.
/// @param listingJson JSON to process.
/// @param clear Whether or not the listing should be cleared before processing the listing.
/// @return True on success. False on failure.
bool process_listing(std::string_view listingJson, bool clear);
/// @brief Checks the json for an error.
/// @param json json_object to check.
/// @return True if an error is found. False if not.
/// @note Error is logged.
bool error_occured(json_object *json);
};
} // namespace remote

122
include/remote/Storage.hpp Normal file
View File

@ -0,0 +1,122 @@
#pragma once
#include "fslib.hpp"
#include "remote/StorageItem.hpp"
#include <curl/curl.h>
#include <string_view>
#include <vector>
namespace remote
{
/// @brief Remote storage base class. All remote storage solutions must derive from this base class.
class Storage
{
public:
/// @brief Constructs a new storage instance.
Storage(void);
/// @brief Destructs the storage instance.
virtual ~Storage();
/// @brief Returns whether or not the remote service was initialized successfully.
/// @return True if it was. False if not.
bool is_initialized(void) const;
/// @brief Returns if a directory exists with the parent and name passed.
/// @param directory Directory to search for.
/// @param parentId ID of the parent directory.
/// @return True if it does. False on failure.
bool directory_exists(std::string_view directory, std::string_view parentId);
/// @brief Returns if a file exists with the parent id passed.
/// @param fileName Name of the file to search for.
/// @param parentId ID of the parent directory.
/// @return True if it does. False if it doesn't.
bool file_exists(std::string_view filename, std::string_view parentId);
/// @brief Gets the id of the directory passed.
/// @param directoryName Name of the directory to get the ID of.
/// @param parentId Parent ID directory of the directory to get.
/// @param idOut String to write the ID to.
/// @return True if the directory is found. False if it doesn't.
bool get_directory_id(std::string_view directoryName, std::string_view parentId, std::string &idOut);
/// @brief Gets the ID of the file name passed.
/// @param filename File name to search for.
/// @param parentId ID of the parent directory.
/// @param idOut String to write the ID to.
/// @return True if a match is found. False if not.
bool get_file_id(std::string_view filename, std::string_view parentId, std::string &idOut);
/// @brief Virtual directory creation function.
/// @param directoryName Name of the directory to create.
/// @param parentId Parent ID of the parent directory.
/// @return True on success. False on failure.
virtual bool create_directory(std::string_view directoryName, std::string_view parentId) = 0;
/// @brief Virtual function for uploading a file to the remote storage service.
/// @param filename Name of the file on the remote storage service.
/// @param parentId Parent ID of the directory to upload to.
/// @param filePath File path of the local file to upload.
/// @return True on success. False on failure.
virtual bool upload_file(std::string_view filename,
std::string_view parentId,
const fslib::Path &filePath) = 0;
/// @brief Virtual function for patching/updating a remote file.
/// @param fileId ID of the file to patch.
/// @param filePath Local path of the file to upload.
/// @return True on success. False on failure.
virtual bool patch_file(std::string_view fileId, const fslib::Path &filePath) = 0;
/// @brief Virtual function for downloading a file from the remote storage service
/// @param fileId ID of the file to download.
/// @param filePath Local path to download the file to.
/// @return True on success. False on failure.
virtual bool download_file(std::string_view fileId, const fslib::Path &filePath) = 0;
/// @brief Virtual function to delete a file from the remote storage service
/// @param fileId ID of the file to delete.
/// @return True on success. False on failure.
virtual bool delete_file(std::string_view fileId) = 0;
protected:
/// @brief This stores whether or not logging in and initialization was successful.
bool m_isInitialized = false;
/// @brief Curl handle.
CURL *m_curl = nullptr;
/// @brief This vector holds the remote listing.
std::vector<remote::StorageItem> m_remoteList;
/// @brief Prepares the curl handle with base of a get request.
void curl_prepare_get(void);
/// @brief Prepares the curl handle with the base of a post.
void curl_prepare_post(void);
/// @brief Prepares the curl handle with a basic upload request.
void curl_prepare_upload(void);
/// @brief Prepares a patch request.
void curl_prepare_patch(void);
/// @brief Calls curl_easy_perform and returns a bool instead of curl error.
/// @return True on success. False on failure.
bool curl_perform(void);
private:
/// @brief Searches for and returns an iterator to the directory (if it's found).
/// @param directoryName Directory to search for.
/// @param parentId Parent ID of the directory.
/// @return Iterator to directory if it's found. .end() if not?
std::vector<remote::StorageItem>::iterator find_directory(std::string_view directoryName,
std::string_view parentId);
/// @brief Searches for and returns an iterator to the file (if it's found).
/// @param filename Name of the file to search for.
/// @param parentId Parent ID of the directory to search in.
/// @return Iterator to file if found. .end() if not?
std::vector<remote::StorageItem>::iterator find_file(std::string_view filename, std::string_view parentId);
};
} // namespace remote

View File

@ -0,0 +1,58 @@
#pragma once
#include <string>
namespace remote
{
/// @brief This class holds the information related to remote files on the storage service.
class StorageItem
{
public:
/// @brief Constructor for storage item.
/// @param name File / Directory name.
/// @param id ID of the item.
/// @param parentId ID of the parent directory.
/// @param size Size of the item.
/// @param isDirectory Whether or not the item is a directory.
StorageItem(std::string_view name,
std::string_view id,
std::string_view parentId,
int size,
bool isDirectory);
/// @brief Function to set the size after patch/update.
/// @param newSize New size of the item.
void set_size(int newSize);
/// @brief Returns the name of the item.
/// @return Name of the item.
std::string_view get_name(void) const;
/// @brief Returns the ID of the item.
/// @return ID of the item.
std::string_view get_id(void) const;
/// @brief Returns the ID of the parent.
/// @return ID of the parent.
std::string_view get_parent(void) const;
/// @brief Returns the size of the item.
/// @return Size of the item.
int get_size(void) const;
/// @brief Returns if the item is a directory.
/// @return Whether or not the item is a directory.
bool is_directory(void) const;
private:
/// @brief Item's name.
std::string m_name;
/// @brief Item's ID.
std::string m_id;
/// @brief ID of the parent.
std::string m_parent;
/// @brief Item's size.
int m_size;
/// @brief Whether or not the item is a directory.
bool m_isDirectory;
};
} // namespace remote

View File

@ -0,0 +1,7 @@
#pragma once
namespace remote
{
/// @brief Initializes the remote storage system.
void initialize(void);
} // namespace remote

View File

@ -22,6 +22,11 @@ namespace stringutil
/// @param replace What to replace the sequence with.
void replace_in_string(std::string &target, std::string_view find, std::string_view replace);
/// @brief Strips a character from the target string.
/// @param c Character to strip.
/// @param target Target to strip the character from.
void strip_character(char c, std::string &target);
/// @brief Attempts to sanitize the string for use with the SD card.
/// @param stringIn String to attempt to sanitize.
/// @param stringOut Buffer to write result to.

View File

@ -48,7 +48,7 @@ JKSV::JKSV(void)
ABORT_ON_FAILURE(initialize_service(romfsInit, "RomFS"));
// Let FsLib take care of calls to SDMC instead of fs_dev
ABORT_ON_FAILURE(fslib::dev::initializeSDMC());
ABORT_ON_FAILURE(fslib::dev::initialize_sdmc());
// SDL
ABORT_ON_FAILURE(sdl::initialize("JKSV", 1280, 720));
@ -73,9 +73,9 @@ JKSV::JKSV(void)
// Get and create working directory. There isn't much of an FS anymore.
fslib::Path workingDirectory = config::get_working_directory();
if (!fslib::directoryExists(workingDirectory) && !fslib::createDirectoriesRecursively(workingDirectory))
if (!fslib::directory_exists(workingDirectory) && !fslib::create_directories_recursively(workingDirectory))
{
logger::log("Error creating working directory: %s", fslib::getErrorString());
logger::log("Error creating working directory: %s", fslib::get_error_string());
return;
}
@ -88,19 +88,19 @@ JKSV::JKSV(void)
}
// Install/setup our color changing characters.
sdl::text::addColorCharacter(L'#', colors::BLUE);
sdl::text::addColorCharacter(L'*', colors::RED);
sdl::text::addColorCharacter(L'<', colors::YELLOW);
sdl::text::addColorCharacter(L'>', colors::GREEN);
sdl::text::addColorCharacter(L'`', colors::BLUE_GREEN);
sdl::text::addColorCharacter(L'^', colors::PINK);
sdl::text::add_color_character(L'#', colors::BLUE);
sdl::text::add_color_character(L'*', colors::RED);
sdl::text::add_color_character(L'<', colors::YELLOW);
sdl::text::add_color_character(L'>', colors::GREEN);
sdl::text::add_color_character(L'`', colors::BLUE_GREEN);
sdl::text::add_color_character(L'^', colors::PINK);
// This is to check whether the author wanted credit for their work.
m_showTranslationInfo =
std::char_traits<char>::compare(strings::get_by_name(strings::names::TRANSLATION_INFO, 1), "NULL", 4) != 0;
// This can't be in an initializer list because it needs SDL initialized.
m_headerIcon = sdl::TextureManager::createLoadTexture("HeaderIcon", "romfs:/Textures/HeaderIcon.png");
m_headerIcon = sdl::TextureManager::create_load_texture("HeaderIcon", "romfs:/Textures/HeaderIcon.png");
// Push initial main menu state.
JKSV::push_state(std::make_shared<MainMenuState>());
@ -148,10 +148,10 @@ void JKSV::update(void)
void JKSV::render(void)
{
sdl::frameBegin(colors::CLEAR_COLOR);
sdl::frame_begin(colors::CLEAR_COLOR);
// Top and bottom divider lines.
sdl::renderLine(NULL, 30, 88, 1250, 88, colors::WHITE);
sdl::renderLine(NULL, 30, 648, 1250, 648, colors::WHITE);
sdl::render_line(NULL, 30, 88, 1250, 88, colors::WHITE);
sdl::render_line(NULL, 30, 648, 1250, 648, colors::WHITE);
// Icon
m_headerIcon->render(NULL, 66, 27);
// "JKSV"
@ -192,7 +192,7 @@ void JKSV::render(void)
// Render messages.
ui::PopMessageManager::render();
sdl::frameEnd();
sdl::frame_end();
}
void JKSV::push_state(std::shared_ptr<AppState> newState)

View File

@ -52,15 +52,15 @@ BackupMenuState::BackupMenuState(data::User *user, data::TitleInfo *titleInfo, F
{
if (!sm_isInitialized)
{
sm_panelWidth = sdl::text::getWidth(22, strings::get_by_name(strings::names::CONTROL_GUIDES, 2)) + 64;
sm_panelWidth = sdl::text::get_width(22, strings::get_by_name(strings::names::CONTROL_GUIDES, 2)) + 64;
// To do: Give classes an alternate so they don't have to be constructed.
sm_backupMenu = std::make_shared<ui::Menu>(8, 8, sm_panelWidth - 14, 24, 600);
sm_slidePanel = std::make_unique<ui::SlideOutPanel>(sm_panelWidth, ui::SlideOutPanel::Side::Right);
sm_menuRenderTarget =
sdl::TextureManager::createLoadTexture("backupMenuTarget",
sm_panelWidth,
600,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
sdl::TextureManager::create_load_texture("backupMenuTarget",
sm_panelWidth,
600,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
sm_isInitialized = true;
}
@ -73,7 +73,7 @@ BackupMenuState::BackupMenuState(data::User *user, data::TitleInfo *titleInfo, F
fslib::Directory saveCheck(fs::DEFAULT_SAVE_PATH);
m_saveHasData = saveCheck.getCount() > 0;
m_saveHasData = saveCheck.get_count() > 0;
BackupMenuState::refresh();
}
@ -113,8 +113,8 @@ void BackupMenuState::update(void)
std::strcat(backupName, ".zip");
}
else if (!config::get_by_key(config::keys::EXPORT_TO_ZIP) && !std::strstr(backupName, ".zip") &&
!fslib::directoryExists(m_directoryPath / backupName) &&
!fslib::createDirectory(m_directoryPath / backupName))
!fslib::directory_exists(m_directoryPath / backupName) &&
!fslib::create_directory(m_directoryPath / backupName))
{
return;
}
@ -162,13 +162,13 @@ void BackupMenuState::update(void)
fslib::Path targetPath = m_directoryPath / m_directoryListing[selected];
// This is a quick check to avoid restoring blanks.
if (fslib::directoryExists(targetPath) && !fs::directory_has_contents(targetPath))
if (fslib::directory_exists(targetPath) && !fs::directory_has_contents(targetPath))
{
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::POP_MESSAGES_BACKUP_MENU, 1));
return;
}
else if (fslib::fileExists(targetPath) && std::strcmp("zip", targetPath.getExtension()) == 0 &&
else if (fslib::file_exists(targetPath) && std::strcmp("zip", targetPath.get_extension()) == 0 &&
!fs::zip_has_contents(targetPath))
{
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
@ -215,7 +215,7 @@ void BackupMenuState::update(void)
}
else if (input::button_pressed(HidNpadButton_B))
{
fslib::closeFileSystem(fs::DEFAULT_SAVE_MOUNT);
fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT);
sm_slidePanel->close();
}
else if (sm_slidePanel->is_closed())
@ -237,8 +237,8 @@ void BackupMenuState::render(void)
// Grab the render target.
SDL_Texture *slideTarget = sm_slidePanel->get();
sdl::renderLine(slideTarget, 10, 42, sm_panelWidth - 10, 42, colors::WHITE);
sdl::renderLine(slideTarget, 10, 648, sm_panelWidth - 10, 648, colors::WHITE);
sdl::render_line(slideTarget, 10, 42, sm_panelWidth - 10, 42, colors::WHITE);
sdl::render_line(slideTarget, 10, 648, sm_panelWidth - 10, 648, colors::WHITE);
sdl::text::render(slideTarget,
32,
673,
@ -267,7 +267,7 @@ void BackupMenuState::refresh(void)
sm_backupMenu->reset();
sm_backupMenu->add_option(strings::get_by_name(strings::names::BACKUP_MENU, 0));
for (int64_t i = 0; i < m_directoryListing.getCount(); i++)
for (int64_t i = 0; i < m_directoryListing.get_count(); i++)
{
sm_backupMenu->add_option(m_directoryListing[i]);
}
@ -304,9 +304,9 @@ static void create_new_backup(sys::ProgressTask *task,
.m_totalSaveSize = fs::get_directory_total_size(fs::DEFAULT_SAVE_PATH)};
// This extension search is lazy and needs to be revised.
if (config::get_by_key(config::keys::EXPORT_TO_ZIP) || std::strcmp("zip", targetPath.getExtension()) == 0)
if (config::get_by_key(config::keys::EXPORT_TO_ZIP) || std::strstr(targetPath.c_string(), "zip"))
{
zipFile newBackup = zipOpen64(targetPath.cString(), APPEND_STATUS_CREATE);
zipFile newBackup = zipOpen64(targetPath.c_string(), APPEND_STATUS_CREATE);
if (!newBackup)
{
// To do: Pop up.
@ -314,10 +314,13 @@ static void create_new_backup(sys::ProgressTask *task,
return;
}
// Data for save meta.
zip_fileinfo saveMetaInfo = fs::create_zip_fileinfo();
// Write meta to zip.
int zipError = zipOpenNewFileInZip64(newBackup,
".jksv_save_meta.bin",
NULL,
&saveMetaInfo,
NULL,
0,
NULL,
@ -338,7 +341,6 @@ static void create_new_backup(sys::ProgressTask *task,
else
{
{
// Write save meta quick.
fslib::Path saveMetaPath = targetPath / ".jksv_save_meta.bin";
fslib::File saveMetaOut(targetPath, FsOpenMode_Create | FsOpenMode_Write, sizeof(fs::SaveMetaData));
if (saveMetaOut)
@ -346,7 +348,6 @@ static void create_new_backup(sys::ProgressTask *task,
saveMetaOut.write(&saveMeta, sizeof(fs::SaveMetaData));
}
}
fs::copy_directory(fs::DEFAULT_SAVE_PATH, targetPath, 0, {}, task);
}
spawningState->refresh();
@ -355,30 +356,30 @@ static void create_new_backup(sys::ProgressTask *task,
static void overwrite_backup(sys::ProgressTask *task, std::shared_ptr<TargetStruct> dataStruct)
{
// DirectoryExists can also be used to check if the target is a directory.
if (fslib::directoryExists(dataStruct->m_targetPath) &&
!fslib::deleteDirectoryRecursively(dataStruct->m_targetPath))
// directory_exists can also be used to check if the target is a directory.
if (fslib::directory_exists(dataStruct->m_targetPath) &&
!fslib::delete_directory_recursively(dataStruct->m_targetPath))
{
logger::log("Error overwriting backup: %s", fslib::getErrorString());
logger::log("Error overwriting backup: %s", fslib::get_error_string());
task->finished();
return;
} // This has an added check for the zip extension so it can't try to overwrite files that aren't supposed to be zip.
else if (fslib::fileExists(dataStruct->m_targetPath) &&
std::strcmp("zip", dataStruct->m_targetPath.getExtension()) == 0 &&
!fslib::deleteFile(dataStruct->m_targetPath))
else if (fslib::file_exists(dataStruct->m_targetPath) &&
std::strcmp("zip", dataStruct->m_targetPath.get_extension()) == 0 &&
!fslib::delete_file(dataStruct->m_targetPath))
{
logger::log("Error overwriting backup: %s", fslib::getErrorString());
logger::log("Error overwriting backup: %s", fslib::get_error_string());
task->finished();
return;
}
if (std::strcmp("zip", dataStruct->m_targetPath.getExtension()))
if (std::strcmp("zip", dataStruct->m_targetPath.get_extension()))
{
zipFile backupZip = zipOpen64(dataStruct->m_targetPath.cString(), APPEND_STATUS_CREATE);
zipFile backupZip = zipOpen64(dataStruct->m_targetPath.c_string(), APPEND_STATUS_CREATE);
fs::copy_directory_to_zip(fs::DEFAULT_SAVE_PATH, backupZip, task);
zipClose(backupZip, NULL);
} // I hope this check works for making sure this is a folder
else if (dataStruct->m_targetPath.getExtension() == nullptr && fslib::createDirectory(dataStruct->m_targetPath))
else if (dataStruct->m_targetPath.get_extension() == nullptr && fslib::create_directory(dataStruct->m_targetPath))
{
fs::copy_directory(fs::DEFAULT_SAVE_PATH, dataStruct->m_targetPath, 0, {}, task);
}
@ -388,16 +389,16 @@ static void overwrite_backup(sys::ProgressTask *task, std::shared_ptr<TargetStru
static void restore_backup(sys::ProgressTask *task, std::shared_ptr<TargetStruct> dataStruct)
{
// Wipe the save root first.
if (!fslib::deleteDirectoryRecursively(fs::DEFAULT_SAVE_PATH))
if (!fslib::delete_directory_recursively(fs::DEFAULT_SAVE_PATH))
{
logger::log("Error restoring save: %s", fslib::getErrorString());
logger::log("Error restoring save: %s", fslib::get_error_string());
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::POP_MESSAGES_BACKUP_MENU, 2));
task->finished();
return;
}
if (fslib::directoryExists(dataStruct->m_targetPath))
if (fslib::directory_exists(dataStruct->m_targetPath))
{
fs::copy_directory(dataStruct->m_targetPath,
fs::DEFAULT_SAVE_PATH,
@ -405,9 +406,9 @@ static void restore_backup(sys::ProgressTask *task, std::shared_ptr<TargetStruct
fs::DEFAULT_SAVE_MOUNT,
task);
}
else if (std::strstr(dataStruct->m_targetPath.cString(), ".zip") != NULL)
else if (std::strstr(dataStruct->m_targetPath.c_string(), ".zip") != NULL)
{
unzFile targetZip = unzOpen64(dataStruct->m_targetPath.cString());
unzFile targetZip = unzOpen64(dataStruct->m_targetPath.c_string());
if (!targetZip)
{
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
@ -442,19 +443,19 @@ static void delete_backup(sys::Task *task, std::shared_ptr<TargetStruct> dataStr
{
if (task)
{
task->set_status(strings::get_by_name(strings::names::DELETING_FILES, 0), dataStruct->m_targetPath.cString());
task->set_status(strings::get_by_name(strings::names::DELETING_FILES, 0), dataStruct->m_targetPath.c_string());
}
if (fslib::directoryExists(dataStruct->m_targetPath) &&
!fslib::deleteDirectoryRecursively(dataStruct->m_targetPath))
if (fslib::directory_exists(dataStruct->m_targetPath) &&
!fslib::delete_directory_recursively(dataStruct->m_targetPath))
{
logger::log("Error deleting folder backup: %s", fslib::getErrorString());
logger::log("Error deleting folder backup: %s", fslib::get_error_string());
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::POP_MESSAGES_BACKUP_MENU, 4));
}
else if (!fslib::deleteFile(dataStruct->m_targetPath))
else if (fslib::file_exists(dataStruct->m_targetPath) && !fslib::delete_file(dataStruct->m_targetPath))
{
logger::log("Error deleting backup: %s", fslib::getErrorString());
logger::log("Error deleting backup: %s", fslib::get_error_string());
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::POP_MESSAGES_BACKUP_MENU, 4));
}

View File

@ -12,10 +12,10 @@ namespace
ExtrasMenuState::ExtrasMenuState(void)
: m_extrasMenu(32, 8, 1000, 24, 555),
m_renderTarget(sdl::TextureManager::createLoadTexture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET))
m_renderTarget(sdl::TextureManager::create_load_texture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET))
{
const char *extrasString = nullptr;
int currentString = 0;

View File

@ -14,13 +14,14 @@
#include "strings.hpp"
MainMenuState::MainMenuState(void)
: m_renderTarget(sdl::TextureManager::createLoadTexture("MainMenuTarget",
200,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)),
m_background(sdl::TextureManager::createLoadTexture("MainMenuBackground", "romfs:/Textures/MenuBackground.png")),
: m_renderTarget(sdl::TextureManager::create_load_texture("MainMenuTarget",
200,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)),
m_background(
sdl::TextureManager::create_load_texture("MainMenuBackground", "romfs:/Textures/MenuBackground.png")),
m_mainMenu(50, 15, 555), m_controlGuide(strings::get_by_name(strings::names::CONTROL_GUIDES, 0)),
m_controlGuideX(1220 - sdl::text::getWidth(22, m_controlGuide))
m_controlGuideX(1220 - sdl::text::get_width(22, m_controlGuide))
{
// Fetch user list.
data::get_users(sm_users);
@ -44,8 +45,8 @@ MainMenuState::MainMenuState(void)
sm_states.push_back(std::make_shared<ExtrasMenuState>());
// Create icons for the other two.
m_settingsIcon = sdl::TextureManager::createLoadTexture("SettingsIcon", "romfs:/Textures/SettingsIcon.png");
m_extrasIcon = sdl::TextureManager::createLoadTexture("ExtrasIcon", "romfs:/Textures/ExtrasIcon.png");
m_settingsIcon = sdl::TextureManager::create_load_texture("SettingsIcon", "romfs:/Textures/SettingsIcon.png");
m_extrasIcon = sdl::TextureManager::create_load_texture("ExtrasIcon", "romfs:/Textures/ExtrasIcon.png");
// Finally add them to the end.
m_mainMenu.add_option(m_settingsIcon);

View File

@ -5,7 +5,7 @@
#include "strings.hpp"
#include "stringutil.hpp"
#include "ui/PopMessageManager.hpp"
#include "ui/renderFunctions.hpp"
#include "ui/render_functions.hpp"
#include <cmath>
void ProgressState::update(void)
@ -23,19 +23,19 @@ void ProgressState::update(void)
m_progressBarWidth = std::ceil(656.0f * m_task.get_current());
m_progress = std::ceil(m_task.get_current() * 100);
m_percentageString = stringutil::get_formatted_string("%u", m_progress);
m_percentageX = 640 - (sdl::text::getWidth(18, m_percentageString.c_str()));
m_percentageX = 640 - (sdl::text::get_width(18, m_percentageString.c_str()));
}
void ProgressState::render(void)
{
// This will dim the background.
sdl::renderRectFill(NULL, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
sdl::render_rect_fill(NULL, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
// Render the dialog and little loading bar thingy.
ui::render_dialog_box(NULL, 280, 262, 720, 256);
sdl::text::render(NULL, 312, 288, 18, 648, colors::WHITE, m_task.get_status().c_str());
sdl::renderRectFill(NULL, 312, 462, 656, 32, colors::BLACK);
sdl::renderRectFill(NULL, 312, 462, m_progressBarWidth, 32, colors::GREEN);
sdl::render_rect_fill(NULL, 312, 462, 656, 32, colors::BLACK);
sdl::render_rect_fill(NULL, 312, 462, m_progressBarWidth, 32, colors::GREEN);
sdl::text::render(NULL,
m_percentageX,
468,

View File

@ -16,6 +16,7 @@
// This sorts the vector alphabetically so stuff is easier to find
static bool compare_info(data::TitleInfo *infoA, data::TitleInfo *infoB)
{
// Get pointers to both titles.
const char *titleA = infoA->get_title();
const char *titleB = infoB->get_title();

View File

@ -20,11 +20,11 @@ static const char *get_sort_type_text(uint8_t value);
SettingsState::SettingsState(void)
: m_settingsMenu(32, 8, 1000, 24, 555),
m_renderTarget(sdl::TextureManager::createLoadTexture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)),
m_controlGuideX(1220 - sdl::text::getWidth(22, strings::get_by_name(strings::names::CONTROL_GUIDES, 3)))
m_renderTarget(sdl::TextureManager::create_load_texture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)),
m_controlGuideX(1220 - sdl::text::get_width(22, strings::get_by_name(strings::names::CONTROL_GUIDES, 3)))
{
// Loop and allocation the strings and menu options.
int currentString = 0;

View File

@ -24,9 +24,9 @@ void TaskState::render(void)
// Grab task string.
std::string status = m_task.get_status();
// Center so it looks perty
int statusX = 640 - (sdl::text::getWidth(24, status.c_str()) / 2);
int statusX = 640 - (sdl::text::get_width(24, status.c_str()) / 2);
// Dim the background states.
sdl::renderRectFill(NULL, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
sdl::render_rect_fill(NULL, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
// Render the status.
sdl::text::render(NULL, statusX, 351, 24, sdl::text::NO_TEXT_WRAP, colors::WHITE, status.c_str());
}

View File

@ -14,10 +14,10 @@ namespace
TextTitleSelectState::TextTitleSelectState(data::User *user)
: TitleSelectCommon(), m_user(user), m_titleSelectMenu(32, 8, 1000, 20, 555),
m_renderTarget(sdl::TextureManager::createLoadTexture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET))
m_renderTarget(sdl::TextureManager::create_load_texture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET))
{
TextTitleSelectState::refresh();
}

View File

@ -14,7 +14,7 @@ TitleInfoState::TitleInfoState(data::User *user, data::TitleInfo *titleInfo)
}
// Check if the title is too large to fit within the target.
m_titleWidth = sdl::text::getWidth(32, m_titleInfo->get_title());
m_titleWidth = sdl::text::get_width(32, m_titleInfo->get_title());
if (m_titleWidth > 480)
{
// Just set this to 8 and we'll scroll the title.
@ -67,5 +67,5 @@ void TitleInfoState::render(void)
// If the title doesn't need to be scrolled, just render it.
sdl::renderLine(sm_slidePanel->get(), 10, 42, 460, 42, colors::WHITE);
sdl::render_line(sm_slidePanel->get(), 10, 42, 460, 42, colors::WHITE);
}

View File

@ -162,7 +162,7 @@ static void delete_all_backups_for_title(sys::Task *task, std::shared_ptr<Target
dataStruct->m_targetTitle->get_title());
// Just call this and nuke the folder.
if (!fslib::deleteDirectoryRecursively(titlePath))
if (!fslib::delete_directory_recursively(titlePath))
{
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::TITLE_OPTION_POPS, 1));
@ -179,11 +179,11 @@ static void delete_all_backups_for_title(sys::Task *task, std::shared_ptr<Target
static void reset_save_data(sys::Task *task, std::shared_ptr<TargetStruct> dataStruct)
{
// Attempt to mount save.
if (!fslib::openSaveFileSystemWithSaveDataInfo(
if (!fslib::open_save_data_with_save_info(
fs::DEFAULT_SAVE_MOUNT,
*dataStruct->m_targetUser->get_save_info_by_id(dataStruct->m_targetTitle->get_application_id())))
{
logger::log(ERROR_RESETTING_SAVE, fslib::getErrorString());
logger::log(ERROR_RESETTING_SAVE, fslib::get_error_string());
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::TITLE_OPTION_POPS, 2));
task->finished();
@ -191,10 +191,10 @@ static void reset_save_data(sys::Task *task, std::shared_ptr<TargetStruct> dataS
}
// Wipe the root.
if (!fslib::deleteDirectoryRecursively(fs::DEFAULT_SAVE_PATH))
if (!fslib::delete_directory_recursively(fs::DEFAULT_SAVE_PATH))
{
fslib::closeFileSystem(fs::DEFAULT_SAVE_MOUNT);
logger::log(ERROR_RESETTING_SAVE, fslib::getErrorString());
fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT);
logger::log(ERROR_RESETTING_SAVE, fslib::get_error_string());
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::TITLE_OPTION_POPS, 2));
task->finished();
@ -202,10 +202,10 @@ static void reset_save_data(sys::Task *task, std::shared_ptr<TargetStruct> dataS
}
// Attempt commit.
if (!fslib::commitDataToFileSystem(fs::DEFAULT_SAVE_MOUNT))
if (!fslib::commit_data_to_file_system(fs::DEFAULT_SAVE_MOUNT))
{
fslib::closeFileSystem(fs::DEFAULT_SAVE_MOUNT);
logger::log(ERROR_RESETTING_SAVE, fslib::getErrorString());
fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT);
logger::log(ERROR_RESETTING_SAVE, fslib::get_error_string());
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::TITLE_OPTION_POPS, 2));
task->finished();
@ -213,7 +213,7 @@ static void reset_save_data(sys::Task *task, std::shared_ptr<TargetStruct> dataS
}
// Should be good to go.
fslib::closeFileSystem(fs::DEFAULT_SAVE_MOUNT);
fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT);
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::TITLE_OPTION_POPS, 3));
task->finished();
@ -253,10 +253,10 @@ static void change_output_path(data::TitleInfo *targetTitle)
// Rename folder to match so there are no issues.
fslib::Path oldPath = config::get_working_directory() / targetTitle->get_path_safe_title();
fslib::Path newPath = config::get_working_directory() / pathBuffer;
if (fslib::directoryExists(oldPath) && !fslib::renameDirectory(oldPath, newPath))
if (fslib::directory_exists(oldPath) && !fslib::rename_directory(oldPath, newPath))
{
// Bail if this fails, because something is really wrong.
logger::log("Error setting new output path: %s", fslib::getErrorString());
logger::log("Error setting new output path: %s", fslib::get_error_string());
return;
}

View File

@ -7,7 +7,7 @@ TitleSelectCommon::TitleSelectCommon(void)
{
if (m_titleControlsX == 0)
{
m_titleControlsX = 1220 - sdl::text::getWidth(22, strings::get_by_name(strings::names::CONTROL_GUIDES, 1));
m_titleControlsX = 1220 - sdl::text::get_width(22, strings::get_by_name(strings::names::CONTROL_GUIDES, 1));
}
}

View File

@ -21,10 +21,10 @@ namespace
TitleSelectState::TitleSelectState(data::User *user)
: TitleSelectCommon(), m_user(user),
m_renderTarget(sdl::TextureManager::createLoadTexture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)),
m_renderTarget(sdl::TextureManager::create_load_texture(SECONDARY_TARGET,
1080,
555,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET)),
m_titleView(m_user) {};
void TitleSelectState::update(void)
@ -41,8 +41,8 @@ void TitleSelectState::update(void)
// Path to output to.
fslib::Path targetPath = config::get_working_directory() / titleInfo->get_path_safe_title();
if ((fslib::directoryExists(targetPath) || fslib::createDirectory(targetPath)) &&
fslib::openSaveFileSystemWithSaveDataInfo(fs::DEFAULT_SAVE_MOUNT, *saveInfo))
if ((fslib::directory_exists(targetPath) || fslib::create_directory(targetPath)) &&
fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *saveInfo))
{
JKSV::push_state(std::make_shared<BackupMenuState>(m_user,
titleInfo,
@ -50,7 +50,7 @@ void TitleSelectState::update(void)
}
else
{
logger::log("%s", fslib::getErrorString());
logger::log("%s", fslib::get_error_string());
}
}
else if (input::button_pressed(HidNpadButton_X))

View File

@ -176,24 +176,24 @@ static void backup_all_for_user(sys::ProgressTask *task, std::shared_ptr<UserStr
// Try to create target game folder.
fslib::Path gameFolder = config::get_working_directory() / currentTitle->get_path_safe_title();
if (!fslib::directoryExists(gameFolder) && !fslib::createDirectory(gameFolder))
if (!fslib::directory_exists(gameFolder) && !fslib::create_directory(gameFolder))
{
continue;
}
// Try to mount save data.
bool saveMounted =
fslib::openSaveFileSystemWithSaveDataInfo(fs::DEFAULT_SAVE_MOUNT, *targetUser->get_save_info_at(i));
fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *targetUser->get_save_info_at(i));
// Check to make sure the save actually has data to avoid blanks.
{
fslib::Directory saveCheck(fs::DEFAULT_SAVE_PATH);
if (saveMounted && saveCheck.getCount() <= 0)
if (saveMounted && saveCheck.get_count() <= 0)
{
// Gonna borrow these messages. No point in repeating them.
ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_MESSAGE_TICKS,
strings::get_by_name(strings::names::POP_MESSAGES_BACKUP_MENU, 0));
fslib::closeFileSystem(fs::DEFAULT_SAVE_MOUNT);
fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT);
continue;
}
}
@ -204,10 +204,10 @@ static void backup_all_for_user(sys::ProgressTask *task, std::shared_ptr<UserStr
targetUser->get_path_safe_nickname() +
" - " + stringutil::get_date_string() + ".zip";
zipFile targetZip = zipOpen64(targetPath.cString(), APPEND_STATUS_CREATE);
zipFile targetZip = zipOpen64(targetPath.c_string(), APPEND_STATUS_CREATE);
if (!targetZip)
{
logger::log("Error creating zip: %s", fslib::getErrorString());
logger::log("Error creating zip: %s", fslib::get_error_string());
continue;
}
fs::copy_directory_to_zip(fs::DEFAULT_SAVE_PATH, targetZip, task);
@ -219,9 +219,9 @@ static void backup_all_for_user(sys::ProgressTask *task, std::shared_ptr<UserStr
targetUser->get_path_safe_nickname() +
" - " + stringutil::get_date_string();
if (!fslib::createDirectory(targetPath))
if (!fslib::create_directory(targetPath))
{
logger::log("Error creating backup directory: %s", fslib::getErrorString());
logger::log("Error creating backup directory: %s", fslib::get_error_string());
continue;
}
fs::copy_directory(fs::DEFAULT_SAVE_PATH, targetPath, 0, {}, task);
@ -229,7 +229,7 @@ static void backup_all_for_user(sys::ProgressTask *task, std::shared_ptr<UserStr
if (saveMounted)
{
fslib::closeFileSystem(fs::DEFAULT_SAVE_MOUNT);
fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT);
}
}
task->finished();

View File

@ -52,9 +52,9 @@ static void read_array_to_vector(std::vector<uint64_t> &vector, json_object *arr
void config::initialize(void)
{
if (!fslib::directoryExists(CONFIG_FOLDER) && !fslib::createDirectoriesRecursively(CONFIG_FOLDER))
if (!fslib::directory_exists(CONFIG_FOLDER) && !fslib::create_directories_recursively(CONFIG_FOLDER))
{
logger::log("Error creating config folder: %s.", fslib::getErrorString());
logger::log("Error creating config folder: %s.", fslib::get_error_string());
config::reset_to_default();
return;
}
@ -62,7 +62,7 @@ void config::initialize(void)
json::Object configJSON = json::new_object(json_object_from_file, CONFIG_PATH);
if (!configJSON)
{
logger::log("Error opening config for reading: %s", fslib::getErrorString());
logger::log("Error opening config for reading: %s", fslib::get_error_string());
config::reset_to_default();
return;
}
@ -99,7 +99,7 @@ void config::initialize(void)
}
// Load custom output paths.
if (!fslib::fileExists(PATHS_PATH))
if (!fslib::file_exists(PATHS_PATH))
{
// Just bail.
return;
@ -154,7 +154,7 @@ void config::save(void)
json::Object configJSON = json::new_object(json_object_new_object);
// Add working directory first.
json_object *workingDirectory = json_object_new_string(s_workingDirectory.cString());
json_object *workingDirectory = json_object_new_string(s_workingDirectory.c_string());
json_object_object_add(configJSON.get(), config::keys::WORKING_DIRECTORY.data(), workingDirectory);
// Loop through map and add it.

124
source/curl/curl.cpp Normal file
View File

@ -0,0 +1,124 @@
#include "curl.hpp"
#include "logger.hpp"
#include "stringutil.hpp"
// Declarations here. Definitions later.
// Wraps a curl handle in a self freeing unique_ptr
using CurlHandle = std::unique_ptr<CURL, decltype(&curl_easy_cleanup)>;
// Creates a new curl handle.
static CurlHandle new_curl_handle(void);
// This function prepares the base of a get request so I don't have to type so much.
static void prepare_get_request(CURL *curl);
size_t curl::read_from_file(char *buffer, size_t size, size_t count, fslib::File *target)
{
// This just needs to read and return what it already returns.
return target->read(buffer, size * count);
}
size_t curl::write_headers_array(const char *buffer, size_t size, size_t count, curl::HeaderArray *headerArray)
{
// Just push the header back.
headerArray->emplace_back(buffer);
// Return this so curl thinks it worked cause it probably totally did.
return size * count;
}
size_t curl::write_response_string(const char *buffer, size_t size, size_t count, std::string *string)
{
// Append the buffer to the string.
string->append(buffer);
// Return success
return size * count;
}
size_t curl::write_response_buffer(const char *buffer, size_t size, size_t count, curl::DownloadBuffer *bufferOut)
{
// Insert the data to the end of the vector.
bufferOut->insert(bufferOut->end(), reinterpret_cast<unsigned char *>(buffer), size * count);
// Return success.
return size * count;
}
bool curl::download_to_string(std::string_view url, std::string &stringOut)
{
// Curl handle.
CurlHandle curlHandle = new_curl_handle();
// Setup get request.
prepare_get_request(curlHandle.get());
curl_easy_setopt(curlHandle.get(), CURLOPT_URL, url.data());
curl_easy_setopt(curlHandle.get(), CURLOPT_WRITEFUNCTION, write_response_string);
curl_easy_setopt(curlHandle.get(), CURLOPT_WRITEDATA, &stringOut);
// Clear the string first just in case.
stringOut.clear();
if (curl_easy_perform(curlHandle.get()) != CURLE_OK)
{
logger::log("Error getting %s.", url.data());
return false;
}
return true;
}
bool curl::download_to_buffer(std::string_view url, curl::DownloadBuffer &bufferOut)
{
CurlHandle curlHandle = new_curl_handle();
prepare_get_request(curlHandle.get());
curl_easy_setopt(curlHandle.get(), CURLOPT_URL, url.data());
curl_easy_setopt(curlHandle.get(), CURLOPT_WRITEFUNCTION, write_response_buffer);
curl_easy_setopt(curlHandle.get(), CURLOPT_WRITEDATA, &bufferOut);
// Clear the vector first.
bufferOut.clear();
if (curl_easy_perform(curlHandle.get()) != CURLE_OK)
{
logger::log("Error downloading %s.", url.data());
return false;
}
return true;
}
bool curl::extract_value_from_headers(const curl::HeaderArray &headerArray,
std::string_view header,
std::string &valueOut)
{
for (const auto &currentHeader : headerArray)
{
// Check if the current header matches what we need.
if (currentHeader.find(header.data()) != currentHeader.end())
{
// It does. Find the ':'.
size_t headerEnd = currentHeader.find_first_of(':') + 2;
// This should get what's after the ':'.
valueOut = currentHeader.substr(headerEnd, -1);
// Strip and newline chars.
stringutil::strip_character('\n', valueOut);
stringutil::strip_character('\r', valueOut);
return true;
}
}
return false;
}
static CurlHandle new_curl_handle(void)
{
return CurlHandle(curl_easy_init, curl_easy_cleanup);
}
static void prepare_get_request(CURL *curl)
{
// This is just the basics. The other functions fill in the rest.
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, curl::USER_AGENT_STRING.data());
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
}

View File

@ -41,11 +41,11 @@ data::TitleInfo::TitleInfo(uint64_t applicationID) : m_applicationID(application
}
// Create a place holder icon.
int textX = 128 - (sdl::text::getWidth(48, applicationIDHex.c_str()) / 2);
m_icon = sdl::TextureManager::createLoadTexture(applicationIDHex,
256,
256,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
int textX = 128 - (sdl::text::get_width(48, applicationIDHex.c_str()) / 2);
m_icon = sdl::TextureManager::create_load_texture(applicationIDHex,
256,
256,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
m_icon->clear(colors::DIALOG_BOX);
sdl::text::render(m_icon->get(),
textX,
@ -72,9 +72,9 @@ data::TitleInfo::TitleInfo(uint64_t applicationID) : m_applicationID(application
}
// Load the icon.
m_icon = sdl::TextureManager::createLoadTexture(languageEntry->name,
nsControlData.icon,
nsAppControlSize - sizeof(NacpStruct));
m_icon = sdl::TextureManager::create_load_texture(languageEntry->name,
nsControlData.icon,
nsAppControlSize - sizeof(NacpStruct));
}
}

View File

@ -108,7 +108,7 @@ data::User::User(AccountUid accountID,
std::string_view iconPath,
FsSaveDataType saveType)
: m_accountID(accountID), m_saveType(saveType),
m_icon(sdl::TextureManager::createLoadTexture(pathSafeNickname, iconPath.data()))
m_icon(sdl::TextureManager::create_load_texture(pathSafeNickname, iconPath.data()))
{
// We're just gonna use this for both.
std::memcpy(m_nickname, nickname.data(), nickname.length());
@ -242,7 +242,7 @@ void data::User::load_account(AccountProfile &profile, AccountProfileBase &profi
}
// We should be good at this point.
m_icon = sdl::TextureManager::createLoadTexture(profileBase.nickname, iconBuffer.get(), iconSize);
m_icon = sdl::TextureManager::create_load_texture(profileBase.nickname, iconBuffer.get(), iconSize);
// Memcpy the nickname.
std::memcpy(m_nickname, &profileBase.nickname, 0x20);
@ -260,11 +260,11 @@ void data::User::create_account(void)
std::string accountIDString = stringutil::get_formatted_string("Acc_%08X", m_accountID.uid[0] & 0xFFFFFFFF);
// Create icon
int textX = 128 - (sdl::text::getWidth(ICON_FONT_SIZE, accountIDString.c_str()) / 2);
m_icon = sdl::TextureManager::createLoadTexture(accountIDString,
256,
256,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
int textX = 128 - (sdl::text::get_width(ICON_FONT_SIZE, accountIDString.c_str()) / 2);
m_icon = sdl::TextureManager::create_load_texture(accountIDString,
256,
256,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
sdl::text::render(m_icon->get(),
textX,
128 - (ICON_FONT_SIZE / 2),

View File

@ -96,7 +96,7 @@ bool data::initialize(void)
fslib::SaveInfoReader saveInfoReader(SAVE_DATA_SPACE_ORDER[i]);
if (!saveInfoReader)
{
logger::log(fslib::getErrorString());
logger::log(fslib::get_error_string());
continue;
}
@ -141,12 +141,12 @@ bool data::initialize(void)
}
if (config::get_by_key(config::keys::ONLY_LIST_MOUNTABLE) &&
!fslib::openSaveFileSystemWithSaveDataInfo(fs::DEFAULT_SAVE_MOUNT, saveInfo))
!fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, saveInfo))
{
// Continue the loop since mounting failed.
continue;
}
fslib::closeFileSystem(fs::DEFAULT_SAVE_MOUNT);
fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT);
// Find the user with the ID.
auto findUser =

View File

@ -1,4 +1,4 @@
#include "fs/directoryFunctions.hpp"
#include "fs/directory_functions.hpp"
uint64_t fs::get_directory_total_size(const fslib::Path &targetPath)
{
@ -9,16 +9,16 @@ uint64_t fs::get_directory_total_size(const fslib::Path &targetPath)
}
uint64_t directorySize = 0;
for (int64_t i = 0; i < targetDir.getCount(); i++)
for (int64_t i = 0; i < targetDir.get_count(); i++)
{
if (targetDir.isDirectory(i))
if (targetDir.is_directory(i))
{
fslib::Path newTarget = targetPath / targetDir[i];
directorySize += get_directory_total_size(newTarget);
}
else
{
directorySize += targetDir.getEntrySize(i);
directorySize += targetDir.get_entry_size(i);
}
}
return directorySize;
@ -31,5 +31,5 @@ bool fs::directory_has_contents(const fslib::Path &directoryPath)
{
return false;
}
return testDir.getCount() != 0;
return testDir.get_count() != 0;
}

View File

@ -32,7 +32,7 @@ typedef struct
// This function Reads into the buffer. The other thread writes.
static void readThreadFunction(fslib::File &sourceFile, std::shared_ptr<FileTransferStruct> sharedData)
{
int64_t fileSize = sourceFile.getSize();
int64_t fileSize = sourceFile.get_size();
for (int64_t readCount = 0; readCount < fileSize;)
{
// Read data to shared buffer.
@ -56,17 +56,17 @@ void fs::copy_file(const fslib::Path &source,
sys::ProgressTask *task)
{
fslib::File sourceFile(source, FsOpenMode_Read);
fslib::File destinationFile(destination, FsOpenMode_Create | FsOpenMode_Write, sourceFile.getSize());
fslib::File destinationFile(destination, FsOpenMode_Create | FsOpenMode_Write, sourceFile.get_size());
if (!sourceFile || !destinationFile)
{
logger::log("Error opening one of the files: %s", fslib::getErrorString());
logger::log("Error opening one of the files: %s", fslib::get_error_string());
return;
}
// Set status if task pointer was passed.
if (task)
{
task->set_status(strings::get_by_name(strings::names::COPYING_FILES, 0), source.cString());
task->set_status(strings::get_by_name(strings::names::COPYING_FILES, 0), source.c_string());
}
// Shared struct both threads use
@ -80,7 +80,7 @@ void fs::copy_file(const fslib::Path &source,
std::unique_ptr<unsigned char[]> localBuffer(new unsigned char[FILE_BUFFER_SIZE]);
// Get file size for loop and set goal.
int64_t fileSize = sourceFile.getSize();
int64_t fileSize = sourceFile.get_size();
if (task)
{
task->reset(static_cast<double>(fileSize));
@ -111,10 +111,10 @@ void fs::copy_file(const fslib::Path &source,
journalCount = 0;
// Close destination file, commit.
destinationFile.close();
fslib::commitDataToFileSystem(commitDevice);
fslib::commit_data_to_file_system(commitDevice);
// Reopen and seek to previous position since we created it with a size earlier.
destinationFile.open(destination, FsOpenMode_Write);
destinationFile.seek(writeCount, destinationFile.beginning);
destinationFile.seek(writeCount, destinationFile.BEGINNING);
}
// Write to destination
destinationFile.write(localBuffer.get(), readCount);
@ -140,20 +140,20 @@ void fs::copy_directory(const fslib::Path &source,
fslib::Directory sourceDir(source);
if (!sourceDir)
{
logger::log("Error opening directory for reading: %s", fslib::getErrorString());
logger::log("Error opening directory for reading: %s", fslib::get_error_string());
return;
}
for (int64_t i = 0; i < sourceDir.getCount(); i++)
for (int64_t i = 0; i < sourceDir.get_count(); i++)
{
if (sourceDir.isDirectory(i))
if (sourceDir.is_directory(i))
{
fslib::Path newSource = source / sourceDir[i];
fslib::Path newDestination = destination / sourceDir[i];
// Try to create new destination folder and continue loop on failure.
if (!fslib::directoryExists(newDestination) && !fslib::createDirectory(newDestination))
if (!fslib::directory_exists(newDestination) && !fslib::create_directory(newDestination))
{
logger::log("Error creating new destination directory: %s", fslib::getErrorString());
logger::log("Error creating new destination directory: %s", fslib::get_error_string());
continue;
}

View File

@ -1,4 +1,4 @@
#include "fs/saveDataFunctions.hpp"
#include "fs/save_data_functions.hpp"
#include "logger.hpp"
bool fs::create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo)

View File

@ -33,7 +33,7 @@ typedef struct
// Function for reading files for Zipping.
static void zipReadThreadFunction(fslib::File &source, std::shared_ptr<ZipIOStruct> sharedData)
{
int64_t fileSize = source.getSize();
int64_t fileSize = source.get_size();
for (int64_t readCount = 0; readCount < fileSize;)
{
// Read into shared buffer.
@ -74,13 +74,13 @@ void fs::copy_directory_to_zip(const fslib::Path &source, zipFile destination, s
fslib::Directory sourceDir(source);
if (!sourceDir)
{
logger::log("Error opening source directory: %s", fslib::getErrorString());
logger::log("Error opening source directory: %s", fslib::get_error_string());
return;
}
for (int64_t i = 0; i < sourceDir.getCount(); i++)
for (int64_t i = 0; i < sourceDir.get_count(); i++)
{
if (sourceDir.isDirectory(i))
if (sourceDir.is_directory(i))
{
fslib::Path newSource = source / sourceDir[i];
fs::copy_directory_to_zip(newSource, destination, task);
@ -92,29 +92,18 @@ void fs::copy_directory_to_zip(const fslib::Path &source, zipFile destination, s
fslib::File sourceFile(fullSource, FsOpenMode_Read);
if (!sourceFile)
{
logger::log("Error zipping file: %s", fslib::getErrorString());
logger::log("Error zipping file: %s", fslib::get_error_string());
continue;
}
// Date for file(s)
std::time_t timer;
std::time(&timer);
std::tm *localTime = std::localtime(&timer);
zip_fileinfo FileInfo = {.tmz_date = {.tm_sec = localTime->tm_sec,
.tm_min = localTime->tm_min,
.tm_hour = localTime->tm_hour,
.tm_mday = localTime->tm_mday,
.tm_mon = localTime->tm_mon,
.tm_year = localTime->tm_year + 1900},
.dosDate = 0,
.internal_fa = 0,
.external_fa = 0};
// Zip info
zip_fileinfo fileInfo = fs::create_zip_fileinfo();
// Create new file in zip
const char *zipNameBegin = std::strchr(fullSource.getPath(), '/') + 1;
const char *zipNameBegin = std::strchr(fullSource.get_path(), '/') + 1;
int zipError = zipOpenNewFileInZip64(destination,
zipNameBegin,
&FileInfo,
&fileInfo,
NULL,
0,
NULL,
@ -139,13 +128,13 @@ void fs::copy_directory_to_zip(const fslib::Path &source, zipFile destination, s
// Update task if passed.
if (task)
{
task->set_status(strings::get_by_name(strings::names::COPYING_FILES, 1), fullSource.cString());
task->reset(static_cast<double>(sourceFile.getSize()));
task->set_status(strings::get_by_name(strings::names::COPYING_FILES, 1), fullSource.c_string());
task->reset(static_cast<double>(sourceFile.get_size()));
}
std::thread readThread(zipReadThreadFunction, std::ref(sourceFile), sharedData);
int64_t fileSize = sourceFile.getSize();
int64_t fileSize = sourceFile.get_size();
for (int64_t writeCount = 0, readCount = 0; writeCount < fileSize;)
{
{
@ -211,11 +200,11 @@ void fs::copy_zip_to_directory(unzFile source,
// Create full path to item, make sure directories are created if needed.
fslib::Path fullDestination = destination / filename;
fslib::Path directories = fullDestination.subPath(fullDestination.findLastOf('/') - 1);
fslib::Path directories = fullDestination.sub_path(fullDestination.find_last_of('/') - 1);
// To do: Make FsLib handle this correctly. First condition is a workaround for now...
if (directories.isValid() && !fslib::createDirectoriesRecursively(directories))
if (directories.is_valid() && !fslib::create_directories_recursively(directories))
{
logger::log("Error creating zip file path \"%s\": %s", directories.cString(), fslib::getErrorString());
logger::log("Error creating zip file path \"%s\": %s", directories.c_string(), fslib::get_error_string());
continue;
}
@ -224,7 +213,7 @@ void fs::copy_zip_to_directory(unzFile source,
currentFileInfo.uncompressed_size);
if (!destinationFile)
{
logger::log("Error creating file from zip: %s", fslib::getErrorString());
logger::log("Error creating file from zip: %s", fslib::get_error_string());
continue;
}
@ -271,13 +260,13 @@ void fs::copy_zip_to_directory(unzFile source,
// Close.
destinationFile.close();
// Commit
if (!fslib::commitDataToFileSystem(commitDevice))
if (!fslib::commit_data_to_file_system(commitDevice))
{
logger::log("Error committing data to save: %s", fslib::getErrorString());
logger::log("Error committing data to save: %s", fslib::get_error_string());
}
// Reopen, seek to previous position.
destinationFile.open(fullDestination, FsOpenMode_Write);
destinationFile.seek(writeCount, destinationFile.beginning);
destinationFile.seek(writeCount, destinationFile.BEGINNING);
// Reset journal
journalCount = 0;
}
@ -294,16 +283,37 @@ void fs::copy_zip_to_directory(unzFile source,
}
// Close file and commit again just for good measure.
destinationFile.close();
if (!fslib::commitDataToFileSystem(commitDevice))
if (!fslib::commit_data_to_file_system(commitDevice))
{
logger::log("Error performing final file commit: %s", fslib::getErrorString());
logger::log("Error performing final file commit: %s", fslib::get_error_string());
}
} while (unzGoToNextFile(source) != UNZ_END_OF_LIST_OF_FILE);
}
zip_fileinfo fs::create_zip_fileinfo(void)
{
// Grab the current time.
std::time_t currentTime = std::time(NULL);
// Get the local time.
std::tm *localTime = std::localtime(&currentTime);
// Create struct to return.
zip_fileinfo fileInfo = {.tmz_date = {.tm_sec = localTime->tm_sec,
.tm_min = localTime->tm_min,
.tm_hour = localTime->tm_hour,
.tm_mday = localTime->tm_mday,
.tm_mon = localTime->tm_mon,
.tm_year = localTime->tm_year + 1900},
.dosDate = 0,
.internal_fa = 0,
.external_fa = 0};
return fileInfo;
}
bool fs::zip_has_contents(const fslib::Path &zipPath)
{
unzFile testZip = unzOpen(zipPath.cString());
unzFile testZip = unzOpen(zipPath.c_string());
if (!testZip)
{
return false;

View File

@ -0,0 +1,589 @@
#include "remote/GoogleDrive.hpp"
#include "JSON.hpp"
#include "curl/curl.hpp"
#include "logger.hpp"
#include "remote/remote.hpp"
#include <cstring>
namespace
{
// Header strings.
// Content type.
constexpr std::string_view HEADER_CONTENT_TYPE_APP_JSON = "Content-Type: application/json; charset=UTF-8";
// Auth header format.
constexpr std::string_view HEADER_AUTHORIZATION_BEARER = "Authorization: Bearer %s";
// Mime type for folders.
constexpr std::string_view MIME_TYPE_FOLDER = "application/vnd.google-apps.folder";
// This is the URL format used to login with the Switch's web browser.
constexpr std::string_view DRIVE_LOGIN_URL =
"https://accounts.google.com/o/oauth2/v2/"
"auth?client_id=%s&redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto&response_type=code&scope=https:/"
"/www.googleapis.com/auth/drive";
// This is the oauth2 address for [when I remember how this works.]
constexpr std::string_view DRIVE_TOKEN_URL = "https://oauth2.googleapis.com/token";
// Need to figure this out.
constexpr std::string_view DRIVE_APPROVAL_URL = "https://accounts.google.com/o/oauth2/approval";
// The redirect uri
constexpr std::string_view DRIVE_REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob:auto";
// This is the URL format to ping to verify if a token is still valid to use.
constexpr std::string_view DRIVE_TOKEN_VERIFY = "https://oauth2.googleapis.com/tokeninfo?%s";
// This is the api URL to get listings?
constexpr std::string_view DRIVE_FILE_API = "https://www.googleapis.com/drive/v3/files";
// This is the api URL to start an upload.
constexpr std::string_view DRIVE_UPLOAD_API = "https://www.googleapis.com/upload/drive/v3/files";
// Grant string to get an authorization code.
constexpr std::string_view GRANT_AUTH_CODE = "authorization_code";
// Grant string to get a refresh token.
constexpr std::string_view GRANT_REFRESH_TOKEN = "refresh_token";
// JSON keys
constexpr std::string_view JSON_KEY_INSTALLED = "installed";
constexpr std::string_view JSON_KEY_CLIENT_ID = "client_id";
constexpr std::string_view JSON_KEY_CLIENT_SECRET = "client_secret";
constexpr std::string_view JSON_KEY_REFRESH_TOKEN = "refresh_token";
constexpr std::string_view JSON_KEY_AUTH_CODE = "code";
constexpr std::string_view JSON_KEY_REDIRECT_URI = "redirect_uri";
constexpr std::string_view JSON_KEY_GRANT_TYPE = "grant_type";
// This is the default query. I cache the entire drive listing in RAM to make everything smoother.
// I don't like the hitches and pauses from downloading stuff as I go.
constexpr std::string_view DEFAULT_QUERY = "?fields=files(name,id,mimeType,size,parents)&pageSize=1000&q=trashed="
"false\%20and\%20\%27me\%27\%20in\%20owners";
// This is the buffer size for snprintf'ing and formatting URL strings.
constexpr int URL_BUFFER_SIZE = 0x300;
} // namespace
remote::GoogleDrive::GoogleDrive(std::string_view jsonPath)
{
// New json_object wrapped in unique_ptr.
json::Object driveJson = json::new_object(json_object_from_file, jsonPath.data());
if (!driveJson)
{
return;
}
// Everything is wrapped in this.
json_object *installed = json_object_object_get(driveJson.get(), JSON_KEY_INSTALLED.data());
if (!installed)
{
return;
}
// Check for our client strings.
json_object *client = json_object_object_get(driveJson.get(), JSON_KEY_CLIENT_ID.data());
json_object *clientSecret = json_object_object_get(driveJson.get(), JSON_KEY_CLIENT_SECRET.data());
if (!client || !clientSecret)
{
// Just bail because this isn't a valid client_secret file.
return;
}
// Set the strings.
m_clientId = json_object_get_string(client);
m_clientSecret = json_object_get_string(clientSecret);
// Check for a refresh token. If JKSV ran previously and was successful, we should have it here.
json_object *refreshToken = json_object_object_get(driveJson.get(), JSON_KEY_REFRESH_TOKEN.data());
if (refreshToken)
{
m_refreshToken = json_object_get_string(refreshToken);
}
// Init was successful. unique_ptr wrap should take care of json cleanup for us.
m_isInitialized = true;
}
remote::GoogleDrive::~GoogleDrive()
{
curl_slist_free_all(m_headers);
}
bool remote::GoogleDrive::create_directory(std::string_view directoryName, std::string_view parentId)
{
if (!GoogleDrive::token_is_valid() && !GoogleDrive::refresh_token())
{
// There's no real way to continue.
return false;
}
// Construct json post.
json::Object postJson = json::new_object(json_object_new_object);
json_object *dirName = json_object_new_string(directoryName.data());
json_object *mimeType = json_object_new_string(MIME_TYPE_FOLDER);
json_object_object_add(postJson.get(), "name", dirName);
json_object_object_add(postJson.get(), "mimeType", mimeType);
// Append the parent if it's present.
if (!parentId.empty())
{
// This is so stupid. Why is it an array if there's only one parent?
json_object *parentArray = json_object_new_array();
json_object *parent = json_object_new_string(parentId.data());
json_object_array_add(parentArray, parent);
json_object_object_add(postJson.get(), parentArray);
}
std::string response;
Storage::curl_prepare_post();
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headers);
curl_easy_setopt(m_curl, CURLOPT_URL, DRIVE_UPLOAD_API);
curl_easy_setopt(m_curl, CURLTOP_POSTFIELDS, json_object_get_string(postJson.get()));
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
if (!Storage::curl_perform())
{
return false;
}
// Parse the response.
json::Object responseParser = json::new_object(json_tokener_parse, response.c_str());
if (!responseParser || GoogleDrive::error_occured(responseParser.get()))
{
return false;
}
// I do this in memory instead of pulling a new listing so everything is smoother.
json_object *directoryId = json_object_object_get(responseParser.get(), "id");
// Gonna be safe.
if (!directoryId)
{
logger::log("GoogleDrive: Error creating directory: No directory ID found in response!");
return false;
}
// Just push a new directory using the id.
m_remoteList.emplace(directoryName, json_object_get_string(directoryId), parentId, 0, true);
return true;
}
bool remote::GoogleDrive::upload_file(std::string_view filename, std::string_view parentId, fslib::File &target)
{
if (!GoogleDrive::token_is_valid() && !GoogleDrive::refresh_token())
{
return false;
}
// Create the full upload API URL.
char uploadUrl[URL_BUFFER_SIZE] = {0};
std::snprintf(uploadUrl, URL_BUFFER_SIZE, "%s?uploadType=resumable", DRIVE_UPLOAD_API);
// Json
json::Object postJson = json::new_object(json_object_new_object);
if (!postJson)
{
return false;
}
json_object *filenameString = json_object_new_string(filename.data());
json_object_object_add(postJson.get(), "name", filenameString);
if (!parentId.empty())
{
json_object *parentArray = json_object_new_array();
json_object *parentIdString = json_object_new_string(parentId.data());
json_object_array_add(parentArray, parentIdString);
json_object_object_add(parentArray);
}
// Need the headers from this.
curl::HeaderArray headers;
// Prepare base post.
Storage::curl_prepare_post();
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headers);
curl_easy_setopt(m_curl, CURLOPT_URL, uploadUrl);
curl_easy_setopt(m_curl, CURLOPT_HEADERFUNCTION, curl::write_headers_array);
curl_easy_setopt(m_curl, CURLOPT_HEADERDATA, &headers);
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, json_object_get_string(postJson.get()));
if (!Storage::curl_perform())
{
return false;
}
std::string location;
if (!curl::extract_value_from_headers(headers, "location", location))
{
logger::log("Error finding value in headers!\n");
return false;
}
// Prepare to upload.
std::string response;
Storage::curl_prepare_upload();
curl_easy_setopt(m_curl, CURLOPT_URL, location.c_str());
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, curl::read_from_file);
curl_easy_setopt(m_curl, CURLOPT_READDATA, &target);
if (!Storage::curl_perform())
{
return false;
}
json::Object responseParser = json::new_object(json_tokener_parse, response.c_str());
if (!responseParser)
{
return false;
}
json_object *fileId = json_object_object_get(responseParser.get(), "id");
json_object *name = json_object_object_get(responseParser.get(), "name");
json_object *mimeType = json_object_object_get(responseParser.get(), "mimeType");
if (!fileId || !name || !mimeType)
{
logger::log("Error uploading file: Invalid response received.");
return false;
}
m_remoteList.emplace_back(json_object_get_string(fileId),
json_object_get_string(fileId),
parentId,
target.get_size(),
false);
return true;
}
bool remote::GoogleDrive::patch_file(std::string_view fileId, fslib::File &target)
{
if (!GoogleDrive::token_is_valid() && !GoogleDrive::refresh_token())
{
return false;
}
char patchUrl[URL_BUFFER_SIZE] = {0};
std::snprintf(patchUrl, URL_BUFFER_SIZE, "%s/%s>uploadType=resumable", DRIVE_UPLOAD_API, fileId.data());
std::string response;
curl::HeaderArray headers;
Storage::curl_prepare_patch();
curl_easy_setopt(m_curl, CURLOPT_URL, patchUrl);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(m_curl, CURLOPT_HEADERFUNCTION, curl::write_headers_array);
curl_easy_setopt(m_curl, CURLOPT_HEADERDATA, &headers);
if (!Storage::curl_perform())
{
return false;
}
std::string location;
if (!curl::extract_value_from_headers(headers, "Location", location))
{
return false;
}
Storage::curl_prepare_upload();
curl_easy_setopt(m_curl, CURLOPT_URL, location.c_str());
curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, curl::read_from_file);
curl_easy_setopt(m_curl, CURLOPT_READDATA, &target);
if (!Storage::curl_perform())
{
return false;
}
// Update the file size in the listing according to the file uploaded. No point in wasting time fetching it.
auto findFile = std::find(m_remoteList.begin(), m_remoteList.end(), [fileId](const remote::StorageItem &item) {
return item.get_id() == fileId;
});
if (findFile == m_remoteList.end())
{
return false;
}
findFile->set_file_size(target.get_size());
return true;
}
bool remote::GoogleDrive::download_file(std::string_view fileId, fslib::File &target)
{
if (!GoogleDrive::token_is_valid() && !GoogleDrive::refresh_token())
{
return false;
}
char downloadUrl[URL_BUFFER_SIZE] = {0};
std::snprintf(downloadUrl, URL_BUFFER_SIZE, "%s/%s?alt=media", DRIVE_FILE_API, fileId.data());
Storage::curl_prepare_get();
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, curl::write_headers_array);
curl_easy_setopt(m_curl, CURLOPT_URL, downloadUrl);
}
bool remote::GoogleDrive::delete_file(std::string_view fileId)
{
}
bool remote::GoogleDrive::sign_in_and_authenticate(std::string &authCodeOut)
{
// This shouldn't be able to happen, but just in case.
if (m_isInitialized)
{
return;
}
// snprint the login url together.
char loginUrl[URL_BUFFER_SIZE] = {0};
std::snprintf(loginUrl, URL_BUFFER_SIZE, DRIVE_LOGIN_URL, m_clientId.c_str());
// We use the reply struct to get the token from the last page.
WebCommonConfig webConfig;
WebCommonReply webReply;
// Hard to read, but screw it. All web stuff in one shot.
if (R_FAILED(webPageCreate(&webConfig, loginUrl)) ||
R_FAILED(webConfigSetCallbackUrl(&webConfig, DRIVE_APPROVAL_URL)) ||
R_FAILED(webConfigShow(&webConfig, &webReply)))
{
logger::log("Error occurred while configuring and launching the web applet");
return false;
}
// Now we get the URL from the web reply and cut out the authentication code.
size_t replyLength = 0;
char replyUrl[URL_BUFFER_SIZE] = {0};
if (R_FAILED(webReplyGetLastUrl(&webReply, replyUrl, URL_BUFFER_SIZE, &replyLength)))
{
logger::log("Error occurred while retrieving the last URL from web reply.");
return false;
}
// Old JKSV used to do this with C++ strings, but here I'm gonna use C functions.
// This should get us to the beginning of the approval code.
char *approvalBegin = std::strstr(replyUrl, "approvalCode") + 13;
if (!approvalBegin)
{
logger::log("Error reading approval code from web reply.");
return false;
}
char *approvalEnd = std::strpbrk(replyUrl, "#&");
if (!approvalEnd)
{
logger::log("Error finding the end of the approval code.");
return false;
}
// This should set the authentication code to the substring in the web reply.
authCodeOut.assign(approvalCode, approvalEnd - approvalBegin);
return true;
}
bool remote::GoogleDrive::exchange_auth_code(std::string_view authCode)
{
// Post json
json::Object postJson = json::new_object(json_object_new_object);
// Doing this with json-c is kinda annoying.
json_object *clientIdString = json_object_new_string(m_clientId.c_str());
json_object *clientSecretString = json_object_new_string(m_clientSecret.c_str());
json_object *authCodeString = json_object_new_string(authCode.data());
json_object *redirectUriString = json_object_new_string(DRIVE_REDIRECT_URI.data());
json_object *grantTypeString = json_object_new_string(GRANT_AUTH_CODE.data());
// Add it to the post json.
json_object_object_add(postJson.get(), "client_id", clientIdString);
json_object_object_add(postJson.get(), "client_secret", clientSecretString);
json_object_object_add(postJson.get(), "code", authCode.data());
json_object_object_add(postJson.get(), "redirect_uri", redirectUriString);
json_object_object_add(postJson.get(), "grant_type", grantTypeString);
// String for post.
std::string response;
// Curl post.
Storage::curl_prepare_post();
curl_easy_setopt(m_curl, CURLOPT_URL, DRIVE_TOKEN_URL.data());
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, json_object_get_string(postJson.get()));
if (!Storage::curl_perform())
{
return false;
}
json::Object responseParser = json::new_object(json_tokener_parse, response.c_str());
if (!responseParser || GoogleDrive::error_occured(responseParser.get()))
{
return false;
}
json_object *accessToken = json_object_object_get(responseParser.get());
json_object *refreshToken = json_object_object_get(responseParser.get());
if (!accessToken || !refreshToken)
{
logger::log("Error exchanging authentication code: Token and/or refresh token not found!");
return false;
}
// Store them
m_token = json_object_get_string(accessToken);
m_refreshToken = json_object_get_string(refresh_token);
// Done? Or are we?
return true;
}
bool remote::GoogleDrive::refresh_token(void)
{
// Post json.
json::Object postJson = json::new_object(json_object_new_object);
// Assemble~
json_object *clientIdString = json_object_new_string(m_clientId.c_str());
json_object *clientSecretString = json_object_new_string(m_clientSecret.c_str());
json_object *refreshTokenString = json_object_new_string(m_refreshToken.c_str());
json_object *grantTypeString = json_object_new_string(GRANT_REFRESH_TOKEN.data());
json_object_object_add(postJson.get(), JSON_KEY_CLIENT_ID.data(), clientIdString);
json_object_object_add(postJson.get(), JSON_KEY_CLIENT_SECRET.data(), clientSecretString);
json_object_object_add(postJson.get(), JSON_KEY_REFRESH_TOKEN.data(), refreshTokenString);
json_object_object_add(postJson.get(), JSON_KEY_GRANT_TYPE.data(), grantTypeString);
// Prepare post.
std::string response;
Storage::curl_prepare_post();
curl_easy_setopt(m_curl, CURLOPT_URL, DRIVE_TOKEN_URL.data());
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, json_object_get_string(postJson.get()));
if (!Storage::curl_perform())
{
return false;
}
json::Object responseParser = json::new_object(json_tokener_parse, response.c_str());
if (!responseParser || GoogleDrive::error_occured(responseParser.get()))
{
return false;
}
json_object *accessToken = json_object_object_get(postJson.get(), "access_token");
if (!accessToken)
{
logger::log("Access token not found!");
return false;
}
m_token = json_object_get_string(accessToken);
return true;
}
bool remote::GoogleDrive::token_is_valid(void)
{
char tokenVerifyUrl[URL_BUFFER_SIZE] = {0};
std::snprintf(tokenVerifyUrl, URL_BUFFER_SIZE, "%saccess_token=%s", m_token.c_str());
std::string response;
Storage::curl_prepare_get();
curl_easy_setopt(m_curl, CURLOPT_URL, tokenVerifyUrl);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
if (!Storage::curl_perform())
{
return false;
}
json::Object responseParser = json::new_object(json_tokener_parse, response.c_str());
if (!responseParser || GoogleDrive::error_occured(responseParser.get()))
{
return false;
}
// I'm assuming no error found means everything is fine.
return true;
}
bool remote::GoogleDrive::request_listing(const char *listingUrl, std::string &jsonOut)
{
Storage::curl_prepare_get();
curl_easy_setopt(m_curl, CURLOPT_URL, listingUrl);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curl::write_response_string);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &jsonOut);
return Storage::curl_perform();
}
bool remote::GoogleDrive::process_listing(std::string_view listingJson, bool clear)
{
json::Object listParser = json::new_object(json_tokener_parse, listingJson.data());
json_object *fileArray = nullptr;
// Check both at once.
if (!listParser || !(files = json_object_object_get(listParser.get(), "files")))
{
logger::log("Error parsing Google Drive listing!");
return false;
}
size_t listCount = json_object_array_length(listParser.get());
if (listCount == 0)
{
logger::log("Google Drive listing is empty!");
return false;
}
// Clear and reserve if desired.
if (clear)
{
m_remoteList.clear();
m_remoteList.reserve(listCount);
}
for (size_t i = 0; i < listCount; i++)
{
json_object *currentListing = json_object_array_get_idx(fileArray, i);
if (!currentListing)
{
logger::log("Error parsing drive listing. THIS SHOULDN'T BE ABLE TO HAPPEN!");
break;
}
// Get our keys.
json_object *idString = json_object_object_get(listParser.get(), "id");
json_object *nameString = json_object_object_get(listParser.get(), "name");
json_object *mimeTypeString = json_object_object_get(listParser.get(), "mimeType");
json_object *sizeString = json_object_object_get(listParser.get(), "size");
// I don't understand why this is a fucking array.
json_object *parentArray = json_object_object_get(listParser.get(), "parents");
json_object *parent = nullptr;
// Parent is held in a array for some reason. This should work fine. Remember to test it, JK.
if (parentArray)
{
size_t parentLength = json_object_array_length(parentArray);
for (size_t i = 0; i < parentLength; i++)
{
// I'm hoping this works how I want. Google making this an array even though items can only really
// have one parent is really stupid.
parent = json_object_array_get_idx(parentArray, i);
}
}
// Emplace
m_remoteList.emplace_back(json_object_get_string(nameString),
json_object_get_string(idString),
json_object_get_string(parent),
json_object_get_int64(sizeString),
std::strcmp(MIME_TYPE_FOLDER.data(), json_object_get_string(mimeTypeString) == 0));
}
return true;
}
bool remote::GoogleDrive::error_occured(json_object *json)
{
json_object *error = json_object_object_get(json, "error");
if (error)
{
return true;
}
return false;
}

124
source/remote/Storage.cpp Normal file
View File

@ -0,0 +1,124 @@
#include "remote/Storage.hpp"
#include "curl/curl.hpp"
#include "logger.hpp"
#include <algorithm>
namespace
{
/// @brief This is the buffer size used for uploading files.
constexpr size_t CURL_UPLOAD_BUFFER_SIZE = 0x10000;
} // namespace
remote::Storage::Storage(void)
{
m_curl = curl_easy_init();
}
remote::Storage::~Storage()
{
curl_easy_cleanup(m_curl);
}
bool remote::Storage::is_initialized(void) const
{
return m_isInitialized;
}
bool remote::Storage::directory_exists(std::string_view directory, std::string_view parentId)
{
return Storage::find_directory(directory, parentId) != m_remoteList.end();
}
bool remote::Storage::file_exists(std::string_view filename, std::string_view parentId)
{
return Storage::find_file(filename, parentId) != m_remoteList.end();
}
bool remote::Storage::get_directory_id(std::string_view directoryName, std::string_view parentId, std::string &idOut)
{
auto findDirectory = Storage::find_directory(directoryName, parentId);
if (findDirectory == m_remoteList.end())
{
return false;
}
// Assign the string.
idOut.assign(findDirectory->get_id());
return true;
}
bool remote::Storage::get_file_id(std::string_view filename, std::string_view parentId, std::string &idOut)
{
auto findFile = Storage::find_file(filename, parentId);
if (findFile == m_remoteList.end())
{
return false;
}
idOut.assign(findFile->get_id());
return true;
}
void remote::Storage::curl_prepare_get(void)
{
// Reset the handle.
curl_easy_reset(m_curl);
// Prepare the bare, basic request.
curl_easy_setopt(m_curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(m_curl, CURLOPT_USERAGENT, curl::USER_AGENT_STRING.data());
curl_easy_setopt(m_curl, CURLOPT_ACCEPT_ENCODING, "");
}
void remote::Storage::curl_prepare_post(void)
{
// Reset the handle.
curl_easy_reset(m_curl);
// Prepare the post.
curl_easy_setopt(m_curl, CURLOPT_HTTPPOST, 1L);
curl_easy_setopt(m_curl, CURLOPT_USERAGENT, curl::USER_AGENT_STRING.data());
}
void remote::Storage::curl_prepare_upload(void)
{
curl_easy_reset(m_curl);
curl_easy_setopt(m_curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(m_curl, CURLOPT_USERAGENT, curl::USER_AGENT_STRING.data());
curl_easy_setopt(m_curl, CURLOPT_UPLOAD_BUFFERSIZE, CURL_UPLOAD_BUFFER_SIZE);
}
void remote::Storage::curl_prepare_patch(void)
{
curl_easy_reset(m_curl);
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_easy_setopt(m_curl, CURLOPT_USERAGENT, curl::USER_AGENT_STRING.data());
curl_easy_setopt(m_curl, CURLOPT_UPLOAD_BUFFERSIZE, CURL_UPLOAD_BUFFER_SIZE);
}
bool remote::Storage::curl_perform(void)
{
int curlError = curl_easy_perform(m_curl);
if (curlError != CURLE_OK)
{
logger::log("remote::Storage: Error performing curl: %i.", curlError);
return false;
}
return true;
}
std::vector<remote::StorageItem>::iterator remote::Storage::find_directory(std::string_view directoryName,
std::string_view parentId)
{
return std::find_if(m_remoteList.begin(), m_remoteList.end(), [directoryName, parentId](auto &item) {
return item.is_directory() && item.get_name() == directoryName && item.get_parent() == parentId;
});
}
std::vector<remote::StorageItem>::iterator remote::Storage::find_file(std::string_view filename,
std::string_view parentId)
{
return std::find_if(m_remoteList.begin(), m_remoteList.end(), [filename, parentId](auto &item) {
return !item.is_directory() && item.get_name() == filename && item.get_parent() == parentId;
});
}

View File

@ -0,0 +1,38 @@
#include "remote/StorageItem.hpp"
remote::StorageItem::StorageItem(std::string_view name,
std::string_view id,
std::string_view parentId,
int size,
bool isDirectory)
: m_name(name), m_id(id), m_parent(parentId), m_size(size), m_isDirectory(isDirectory) {};
void remote::StorageItem::set_size(int newSize)
{
m_size = newSize;
}
std::string_view remote::StorageItem::get_name(void) const
{
return m_name;
}
std::string_view remote::StorageItem::get_id(void) const
{
return m_id;
}
std::string_view remote::StorageItem::get_parent(void) const
{
return m_parent;
}
int remote::StorageItem::get_size(void) const
{
return m_size;
}
bool remote::StorageItem::is_directory(void) const
{
return m_isDirectory;
}

View File

@ -77,7 +77,7 @@ bool strings::initialize()
{
fslib::Path filePath = get_file_path();
json::Object stringJSON = json::new_object(json_object_from_file, filePath.cString());
json::Object stringJSON = json::new_object(json_object_from_file, filePath.c_string());
if (!stringJSON)
{
return false;

View File

@ -36,6 +36,15 @@ void stringutil::replace_in_string(std::string &target, std::string_view find, s
}
}
void stringutil::strip_character(char c, std::string &target)
{
size_t charPosition = 0;
while ((charPosition = target.find_first_of(c, charPosition)) != target.npos)
{
target.erase(target.begin() + charPosition);
}
}
bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, size_t stringOutSize)
{
uint32_t codepoint = 0;

View File

@ -1,6 +1,6 @@
#include "ui/IconMenu.hpp"
#include "colors.hpp"
#include "ui/renderFunctions.hpp"
#include "ui/render_functions.hpp"
ui::IconMenu::IconMenu(int x, int y, int rendertargetHeight) : Menu(x, y, 152, 80, rendertargetHeight) {};
@ -26,10 +26,10 @@ void ui::IconMenu::render(SDL_Texture *target, bool hasFocus)
{
ui::render_bounding_box(target, m_x - 8, tempY - 8, 152, 146, m_colorMod);
}
sdl::renderRectFill(m_optionTarget->get(), 0, 0, 4, 130, {0x00FFC5FF});
sdl::render_rect_fill(m_optionTarget->get(), 0, 0, 4, 130, {0x00FFC5FF});
}
//m_options.at(i)->render(m_optiontarget->Get(), 0, 0);
m_options.at(i)->renderStretched(m_optionTarget->get(), 8, 1, 128, 128);
m_options.at(i)->render_stretched(m_optionTarget->get(), 8, 1, 128, 128);
m_optionTarget->render(target, m_x, tempY);
}
}

View File

@ -2,7 +2,7 @@
#include "colors.hpp"
#include "config.hpp"
#include "input.hpp"
#include "ui/renderFunctions.hpp"
#include "ui/render_functions.hpp"
#include <cmath>
ui::Menu::Menu(int x, int y, int width, int fontSize, int renderTargetHeight)
@ -12,10 +12,10 @@ ui::Menu::Menu(int x, int y, int width, int fontSize, int renderTargetHeight)
// Create render target for options
static int MENU_ID = 0;
std::string menuTargetName = "MENU_" + std::to_string(MENU_ID++);
m_optionTarget = sdl::TextureManager::createLoadTexture(menuTargetName,
m_width,
m_optionHeight,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
m_optionTarget = sdl::TextureManager::create_load_texture(menuTargetName,
m_width,
m_optionHeight,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
// Calculate around how many options can be shown on the render target at once.
m_maxDisplayOptions = (renderTargetHeight - m_originalY) / m_optionHeight;
@ -116,7 +116,7 @@ void ui::Menu::render(SDL_Texture *target, bool hasFocus)
ui::render_bounding_box(target, m_x - 4, tempY - 4, m_width + 8, m_optionHeight + 8, m_colorMod);
}
// render the little rectangle.
sdl::renderRectFill(m_optionTarget->get(), 8, 8, 4, m_optionHeight - 16, colors::BLUE_GREEN);
sdl::render_rect_fill(m_optionTarget->get(), 8, 8, 4, m_optionHeight - 16, colors::BLUE_GREEN);
}
// render text to target.
sdl::text::render(m_optionTarget->get(),

View File

@ -3,7 +3,7 @@
#include "config.hpp"
#include "logger.hpp"
#include "sdl.hpp"
#include "ui/renderFunctions.hpp"
#include "ui/render_functions.hpp"
#include <cstdarg>
namespace
@ -27,7 +27,7 @@ void ui::PopMessageManager::update(void)
// New message.
manager.m_messages.push_back({.m_y = 720,
.m_targetY = 720,
.m_width = sdl::text::getWidth(32, currentMessage.c_str()) + 32,
.m_width = sdl::text::get_width(32, currentMessage.c_str()) + 32,
.m_message = currentMessage,
.m_timer = sys::Timer(displayTicks)});
}

View File

@ -9,10 +9,10 @@ ui::SlideOutPanel::SlideOutPanel(int width, Side side)
{
static int slidePanelTargetID = 0;
std::string panelTargetName = "PanelTarget_" + std::to_string(slidePanelTargetID++);
m_renderTarget = sdl::TextureManager::createLoadTexture(panelTargetName,
width,
720,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
m_renderTarget = sdl::TextureManager::create_load_texture(panelTargetName,
width,
720,
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
}
void ui::SlideOutPanel::update(bool hasFocus)

View File

@ -5,7 +5,7 @@ ui::TextScroll::TextScroll(std::string_view text, int fontSize, int availableWid
: m_text(text.data()), m_y(y), m_fontSize(fontSize), m_textColor(color), m_scrollTimer(3000)
{
// Grab text width first.
m_textWidth = sdl::text::getWidth(m_fontSize, m_text.c_str());
m_textWidth = sdl::text::get_width(m_fontSize, m_text.c_str());
// Check if text needs scrolling for width provided.
if (m_textWidth > availableWidth)

View File

@ -23,7 +23,7 @@ void ui::TitleTile::render(SDL_Texture *target, int x, int y)
int renderX = x - ((m_renderWidth - 128) / 2);
int renderY = y - ((m_renderHeight - 128) / 2);
m_icon->renderStretched(target, renderX, renderY, m_renderWidth, m_renderHeight);
m_icon->render_stretched(target, renderX, renderY, m_renderWidth, m_renderHeight);
if (m_isFavorite)
{
sdl::text::render(target, renderX + 4, renderY + 2, 28, sdl::text::NO_TEXT_WRAP, colors::PINK, "\uE017");

View File

@ -3,7 +3,7 @@
#include "config.hpp"
#include "input.hpp"
#include "logger.hpp"
#include "ui/renderFunctions.hpp"
#include "ui/render_functions.hpp"
#include <cmath>
namespace
@ -103,7 +103,7 @@ void ui::TitleView::render(SDL_Texture *target, bool hasFocus)
// Now render the selected title.
if (hasFocus)
{
sdl::renderRectFill(target, m_selectedX - 23, m_selectedY - 23, 174, 174, colors::CLEAR_COLOR);
sdl::render_rect_fill(target, m_selectedX - 23, m_selectedY - 23, 174, 174, colors::CLEAR_COLOR);
ui::render_bounding_box(target, m_selectedX - 24, m_selectedY - 24, 176, 176, m_colorMod);
}
m_titleTiles.at(m_selected).render(target, m_selectedX, m_selectedY);

View File

@ -1,54 +0,0 @@
#include "ui/renderFunctions.hpp"
#include "colors.hpp"
namespace
{
sdl::SharedTexture s_dialogCorners = nullptr;
sdl::SharedTexture s_menuBoundingCorners = nullptr;
} // namespace
void ui::render_dialog_box(SDL_Texture *target, int x, int y, int width, int height)
{
if (!s_dialogCorners)
{
s_dialogCorners = sdl::TextureManager::createLoadTexture("DialogCorners", "romfs:/Textures/DialogCorners.png");
}
// Top
s_dialogCorners->renderPart(target, x, y, 0, 0, 16, 16);
sdl::renderRectFill(target, x + 16, y, width - 32, 16, colors::DIALOG_BOX);
s_dialogCorners->renderPart(target, (x + width) - 16, y, 16, 0, 16, 16);
// Middle
sdl::renderRectFill(NULL, x, y + 16, width, height - 32, colors::DIALOG_BOX);
// Bottom
s_dialogCorners->renderPart(target, x, (y + height) - 16, 0, 16, 16, 16);
sdl::renderRectFill(NULL, x + 16, (y + height) - 16, width - 32, 16, colors::DIALOG_BOX);
s_dialogCorners->renderPart(NULL, (x + width) - 16, (y + height) - 16, 16, 16, 16, 16);
}
void ui::render_bounding_box(SDL_Texture *target, int x, int y, int width, int height, uint8_t colorMod)
{
if (!s_menuBoundingCorners)
{
s_menuBoundingCorners =
sdl::TextureManager::createLoadTexture("MenuBoundingCorners", "romfs:/Textures/MenuBounding.png");
}
// Setup color.
sdl::Color renderMod = {static_cast<uint32_t>((0x88 + colorMod) << 16 | (0xC5 + (colorMod / 2)) << 8 | 0xFF)};
// This shouldn't fail, but I don't really care if it does.
s_menuBoundingCorners->setColorMod(renderMod);
// Top
s_menuBoundingCorners->renderPart(target, x, y, 0, 0, 8, 8);
sdl::renderRectFill(target, x + 8, y, width - 16, 4, renderMod);
s_menuBoundingCorners->renderPart(target, (x + width) - 8, y, 8, 0, 8, 8);
// Middle
sdl::renderRectFill(target, x, y + 8, 4, height - 16, renderMod);
sdl::renderRectFill(target, (x + width) - 4, y + 8, 4, height - 16, renderMod);
// Bottom
s_menuBoundingCorners->renderPart(target, x, (y + height) - 8, 0, 8, 8, 8);
sdl::renderRectFill(target, x + 8, (y + height) - 4, width - 16, 4, renderMod);
s_menuBoundingCorners->renderPart(target, (x + width) - 8, (y + height) - 8, 8, 8, 8, 8);
}

View File

@ -0,0 +1,55 @@
#include "ui/render_functions.hpp"
#include "colors.hpp"
namespace
{
sdl::SharedTexture s_dialogCorners = nullptr;
sdl::SharedTexture s_menuBoundingCorners = nullptr;
} // namespace
void ui::render_dialog_box(SDL_Texture *target, int x, int y, int width, int height)
{
if (!s_dialogCorners)
{
s_dialogCorners =
sdl::TextureManager::create_load_texture("DialogCorners", "romfs:/Textures/DialogCorners.png");
}
// Top
s_dialogCorners->render_part(target, x, y, 0, 0, 16, 16);
sdl::render_rect_fill(target, x + 16, y, width - 32, 16, colors::DIALOG_BOX);
s_dialogCorners->render_part(target, (x + width) - 16, y, 16, 0, 16, 16);
// Middle
sdl::render_rect_fill(NULL, x, y + 16, width, height - 32, colors::DIALOG_BOX);
// Bottom
s_dialogCorners->render_part(target, x, (y + height) - 16, 0, 16, 16, 16);
sdl::render_rect_fill(NULL, x + 16, (y + height) - 16, width - 32, 16, colors::DIALOG_BOX);
s_dialogCorners->render_part(NULL, (x + width) - 16, (y + height) - 16, 16, 16, 16, 16);
}
void ui::render_bounding_box(SDL_Texture *target, int x, int y, int width, int height, uint8_t colorMod)
{
if (!s_menuBoundingCorners)
{
s_menuBoundingCorners =
sdl::TextureManager::create_load_texture("MenuBoundingCorners", "romfs:/Textures/MenuBounding.png");
}
// Setup color.
sdl::Color renderMod = {static_cast<uint32_t>((0x88 + colorMod) << 16 | (0xC5 + (colorMod / 2)) << 8 | 0xFF)};
// This shouldn't fail, but I don't really care if it does.
s_menuBoundingCorners->set_color_mod(renderMod);
// Top
s_menuBoundingCorners->render_part(target, x, y, 0, 0, 8, 8);
sdl::render_rect_fill(target, x + 8, y, width - 16, 4, renderMod);
s_menuBoundingCorners->render_part(target, (x + width) - 8, y, 8, 0, 8, 8);
// Middle
sdl::render_rect_fill(target, x, y + 8, 4, height - 16, renderMod);
sdl::render_rect_fill(target, (x + width) - 4, y + 8, 4, height - 16, renderMod);
// Bottom
s_menuBoundingCorners->render_part(target, x, (y + height) - 8, 0, 8, 8, 8);
sdl::render_rect_fill(target, x + 8, (y + height) - 4, width - 16, 4, renderMod);
s_menuBoundingCorners->render_part(target, (x + width) - 8, (y + height) - 8, 8, 8, 8, 8);
}