mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-04-25 16:15:11 -05:00
Start remote storage work. (Unbuild. Untested.)
This commit is contained in:
parent
11672043c3
commit
6b2283d45b
|
|
@ -1 +1 @@
|
|||
Subproject commit 77517f2d5f0473113e63795b45f134b2a262f893
|
||||
Subproject commit 1b823061b5a850fc384988443b912005ee9941a2
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit fef8b0927d92a97af47f9e554d4fe310c5de66d8
|
||||
Subproject commit 6fcae125af5734a996d115070d1595f4b5ad0562
|
||||
2
Makefile
2
Makefile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
94
include/curl/curl.hpp
Normal 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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
101
include/remote/GoogleDrive.hpp
Normal file
101
include/remote/GoogleDrive.hpp
Normal 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
122
include/remote/Storage.hpp
Normal 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
|
||||
58
include/remote/StorageItem.hpp
Normal file
58
include/remote/StorageItem.hpp
Normal 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
|
||||
7
include/remote/remote.hpp
Normal file
7
include/remote/remote.hpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
namespace remote
|
||||
{
|
||||
/// @brief Initializes the remote storage system.
|
||||
void initialize(void);
|
||||
} // namespace remote
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
124
source/curl/curl.cpp
Normal 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 ¤tHeader : 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, "");
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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(¤tTime);
|
||||
|
||||
// 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;
|
||||
|
|
|
|||
589
source/remote/GoogleDrive.cpp
Normal file
589
source/remote/GoogleDrive.cpp
Normal 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
124
source/remote/Storage.cpp
Normal 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;
|
||||
});
|
||||
}
|
||||
38
source/remote/StorageItem.cpp
Normal file
38
source/remote/StorageItem.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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)});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
55
source/ui/render_functions.cpp
Normal file
55
source/ui/render_functions.cpp
Normal 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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user