mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-09-09 02:55:23 -05:00
Nothing major
This commit is contained in:
27
inc/appStates/titleOptionState.hpp
Normal file
27
inc/appStates/titleOptionState.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "appStates/appState.hpp"
|
||||
#include "data/data.hpp"
|
||||
#include "ui/ui.hpp"
|
||||
|
||||
class titleOptionState : public appState
|
||||
{
|
||||
public:
|
||||
titleOptionState(data::user *currentUser, data::userSaveInfo *currentUserSaveInfo, data::titleInfo *currentTitleInfo);
|
||||
~titleOptionState();
|
||||
|
||||
void update(void);
|
||||
void render(void);
|
||||
|
||||
private:
|
||||
// Pointer to current user
|
||||
data::user *m_CurrentUser;
|
||||
// Pointer to current user's save info
|
||||
data::userSaveInfo *m_CurrentUserSaveInfo;
|
||||
// Pointer to current title
|
||||
data::titleInfo *m_CurrentTitleInfo;
|
||||
// Slide out panel
|
||||
std::unique_ptr<ui::slidePanel> m_SlidePanel;
|
||||
// Menu
|
||||
std::unique_ptr<ui::menu> m_OptionsMenu;
|
||||
};
|
||||
@@ -16,6 +16,8 @@ namespace data
|
||||
std::string getPathSafeTitle(void);
|
||||
// Returns icon texture
|
||||
SDL_Texture *getIcon(void);
|
||||
// Returns journal size
|
||||
uint64_t getJournalSize(const FsSaveDataType &saveType);
|
||||
|
||||
private:
|
||||
// Title ID is needed for a few things
|
||||
|
||||
@@ -17,4 +17,6 @@ namespace fs
|
||||
void unmountSaveData(void);
|
||||
// Commits to default save path
|
||||
void commitSaveData(void);
|
||||
// Erases current save data from device
|
||||
void eraseSaveData(void);
|
||||
}
|
||||
@@ -9,42 +9,47 @@ namespace fs
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
// Default buffer size used for transfering files
|
||||
static const int FILE_BUFFER_SIZE = 0x400000;
|
||||
// Struct for threads reading/writing threads. Here because other parts of JKSV can share the writeFunction
|
||||
typedef struct
|
||||
namespace file
|
||||
{
|
||||
// File's size
|
||||
uint64_t fileSize;
|
||||
// Mutex
|
||||
std::mutex bufferLock;
|
||||
// Conditional for making sure buffer is empty/full
|
||||
std::condition_variable bufferIsReady;
|
||||
// Bool to check
|
||||
bool bufferIsFull = false;
|
||||
// Buffer shared for transfer
|
||||
std::vector<char> buffer;
|
||||
// Whether file needs to be commited on write
|
||||
bool commitWrite = false;
|
||||
// Journal size
|
||||
uint64_t journalSize = 0;
|
||||
// Current offset
|
||||
uint64_t currentOffset = 0;
|
||||
} threadStruct;
|
||||
// Default buffer size used for transfering files
|
||||
static const int FILE_BUFFER_SIZE = 0x400000;
|
||||
// Struct for threads reading/writing threads. Here because other parts of JKSV can share the writeFunction
|
||||
typedef struct
|
||||
{
|
||||
// File's size
|
||||
uint64_t fileSize;
|
||||
// Mutex
|
||||
std::mutex bufferLock;
|
||||
// Conditional for making sure buffer is empty/full
|
||||
std::condition_variable bufferIsReady;
|
||||
// Bool to check
|
||||
bool bufferIsFull = false;
|
||||
// Buffer shared for transfer
|
||||
std::vector<char> buffer;
|
||||
// Whether file needs to be commited on write
|
||||
bool commitWrite = false;
|
||||
// Journal size
|
||||
uint64_t journalSize = 0;
|
||||
// Current offset
|
||||
uint64_t currentOffset = 0;
|
||||
} threadStruct;
|
||||
|
||||
// Just returns size of file
|
||||
int getFileSize(const std::string &filePath);
|
||||
// These functions are all threaded/task wrapper functions
|
||||
// Copies source to destination.
|
||||
void copyFile(const std::string &source, const std::string &destination);
|
||||
// Copies source to destination, but commits to save device after journalSize bytes have been written
|
||||
void copyFileCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize);
|
||||
// Recusively copies source to destination
|
||||
void copyDirectory(const std::string &source, const std::string &destination);
|
||||
// Recursively copies source to destination
|
||||
void copyDirectoryCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize);
|
||||
// These are functions for threaded copying that other parts of JKSV can use.
|
||||
void readThreadFunction(const std::string &source, std::shared_ptr<threadStruct> sharedStruct);
|
||||
void writeThreadFunction(const std::string &destination, std::shared_ptr<threadStruct> sharedStruct);
|
||||
// Just returns size of file
|
||||
int getFileSize(const std::string &filePath);
|
||||
// These functions are all threaded/task wrapper functions
|
||||
// Copies source to destination.
|
||||
void copyFile(const std::string &source, const std::string &destination);
|
||||
// Copies source to destination, but commits to save device after journalSize bytes have been written
|
||||
void copyFileCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize);
|
||||
// Recusively copies source to destination
|
||||
void copyDirectory(const std::string &source, const std::string &destination);
|
||||
// Recursively copies source to destination
|
||||
void copyDirectoryCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize);
|
||||
// std::filesystem::remove all crashes switch? Had to write my own...
|
||||
void deleteDirectoryRecursively(const std::string &target);
|
||||
// These are functions for threaded copying that other parts of JKSV can use.
|
||||
void readThreadFunction(const std::string &source, std::shared_ptr<threadStruct> sharedStruct);
|
||||
void writeThreadFunction(const std::string &destination, std::shared_ptr<threadStruct> sharedStruct);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,16 @@ namespace fs
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
// Copies and compresses source directory to zipFile passed
|
||||
void copyDirectoryToZip(const std::string &source, zipFile zip);
|
||||
// This is only used to write to save filesystems, so it requires journal size
|
||||
void copyZipToDirectory(unzFile unzip, const std::string &destination, const uint64_t &journalSize);
|
||||
namespace zip
|
||||
{
|
||||
// Copies and compresses source directory to zipFile passed
|
||||
void copyDirectoryToZip(const std::string &source, zipFile zip);
|
||||
// This is only used to write to save filesystems, so it requires journal size
|
||||
void copyZipToDirectory(unzFile unzip, const std::string &destination, const uint64_t &journalSize);
|
||||
// Returns the total uncompressed sizes of files in zip
|
||||
uint64_t getZipTotalFileSize(unzFile unzip);
|
||||
// Returns if it's possible to get to a first file in unz
|
||||
bool zipFileIsNotEmpty(unzFile unzip);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,8 @@
|
||||
#define LANG_TITLE_OPTIONS_MENU "titleOptions"
|
||||
#define LANG_SAVE_TYPE_MAIN_MENU "saveTypeMainMenu"
|
||||
|
||||
#define LANG_TITLE_OPTIONS_MENU "titleOptions"
|
||||
|
||||
// Thread status strings
|
||||
#define LANG_THREAD_CREATING_SAVE "threadStatusCreatingSaveData"
|
||||
#define LANG_THREAD_COPYING_FILE "threadStatusCopyingFile"
|
||||
|
||||
@@ -25,9 +25,12 @@ struct backupArgs : sys::taskArgs
|
||||
};
|
||||
|
||||
// This is shared by overwrite and restore
|
||||
// To do: Find a better name since this holds more than paths now
|
||||
struct pathArgs : sys::taskArgs
|
||||
{
|
||||
std::string source, destination;
|
||||
// Journal size for commits
|
||||
uint64_t journalSize = 0;
|
||||
// For reloading folder list
|
||||
backupMenuState *sendingState = NULL;
|
||||
};
|
||||
@@ -78,18 +81,16 @@ void createNewBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
|
||||
if (config::getByKey(CONFIG_USE_ZIP) || extension == "zip")
|
||||
{
|
||||
zipFile zipOut = zipOpen64(path.c_str(), 0);
|
||||
fs::io::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, zipOut);
|
||||
fs::io::zip::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, zipOut);
|
||||
zipClose(zipOut, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::filesystem::create_directories(path);
|
||||
fs::io::copyDirectory(fs::DEFAULT_SAVE_MOUNT_DEVICE, path);
|
||||
fs::io::file::copyDirectory(fs::DEFAULT_SAVE_MOUNT_DEVICE, path);
|
||||
}
|
||||
}
|
||||
{
|
||||
logger::log("Backup name empty.");
|
||||
}
|
||||
|
||||
// Reload list to reflect changes
|
||||
argsIn->sendingState->loadDirectoryList();
|
||||
// Task is finished
|
||||
@@ -108,35 +109,38 @@ void overwriteBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
|
||||
|
||||
// Set status, cast args to type
|
||||
task->setThreadStatus("REPLACE THIS LATER");
|
||||
std::shared_ptr<pathArgs> argIn = std::static_pointer_cast<pathArgs>(args);
|
||||
|
||||
// Get a test listing
|
||||
fs::directoryListing testListing(fs::DEFAULT_SAVE_MOUNT_DEVICE);
|
||||
|
||||
// So JKSV doesn't create empty folders
|
||||
int saveFileCount = testListing.getListingCount();
|
||||
std::shared_ptr<pathArgs> argsIn = std::static_pointer_cast<pathArgs>(args);
|
||||
|
||||
// For checking if overwriting a zip
|
||||
std::string fileExtension = stringUtil::getExtensionFromString(argIn->destination);
|
||||
std::string fileExtension = stringUtil::getExtensionFromString(argsIn->destination);
|
||||
|
||||
if (std::filesystem::is_directory(argIn->destination) && saveFileCount > 0)
|
||||
if (std::filesystem::is_directory(argsIn->destination))
|
||||
{
|
||||
// Delete backup then recreate directory
|
||||
std::filesystem::remove_all(argIn->destination);
|
||||
std::filesystem::create_directories(argIn->destination);
|
||||
fs::io::copyDirectory(fs::DEFAULT_SAVE_MOUNT_DEVICE, argIn->destination);
|
||||
// Add trailing slash first.
|
||||
std::string target = argsIn->destination + "/";
|
||||
// std::filesystem::remove_all keeps crashing my switch, so I wrote my own again
|
||||
fs::io::file::deleteDirectoryRecursively(target);
|
||||
// Recreate whole path.
|
||||
std::filesystem::create_directories(argsIn->destination);
|
||||
// Create the new backup in the same folder
|
||||
fs::io::file::copyDirectory(fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->destination);
|
||||
}
|
||||
else if (std::filesystem::is_directory(argIn->destination) == false && fileExtension == "zip" && saveFileCount > 0)
|
||||
else if (std::filesystem::is_directory(argsIn->destination) == false && fileExtension == "zip")
|
||||
{
|
||||
std::filesystem::remove(argIn->destination);
|
||||
zipFile zipOut = zipOpen64(argIn->destination.c_str(), 0);
|
||||
fs::io::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, zipOut);
|
||||
// Just delete zip file
|
||||
std::filesystem::remove(argsIn->destination);
|
||||
// Create a new one with the same name
|
||||
zipFile zipOut = zipOpen64(argsIn->destination.c_str(), 0);
|
||||
// Create new zip with old name
|
||||
fs::io::zip::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, zipOut);
|
||||
zipClose(zipOut, "");
|
||||
}
|
||||
task->finished();
|
||||
}
|
||||
|
||||
// Wipes current save for game, then copies backup from SD to filesystem
|
||||
// Note: Needs to be tested better some time
|
||||
void restoreBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
@@ -145,21 +149,45 @@ void restoreBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
|
||||
return;
|
||||
}
|
||||
|
||||
// Set status, cast args
|
||||
task->setThreadStatus("REPLACE THIS LATER");
|
||||
std::shared_ptr<pathArgs> argIn = std::static_pointer_cast<pathArgs>(args);
|
||||
std::shared_ptr<pathArgs> argsIn = std::static_pointer_cast<pathArgs>(args);
|
||||
|
||||
// Test if there are actually files in the save
|
||||
fs::directoryListing testListing(argIn->source);
|
||||
int saveFileCount = testListing.getListingCount();
|
||||
if (saveFileCount <= 0)
|
||||
// Get extension before we try to unzip something that isn't a zip
|
||||
std::string fileExtension = stringUtil::getExtensionFromString(argsIn->source);
|
||||
|
||||
if(std::filesystem::is_directory(argsIn->source))
|
||||
{
|
||||
return;
|
||||
// Add trailing slash
|
||||
std::string source = argsIn->source + "/";
|
||||
// Get a test listing first to prevent writing an empty folder to save
|
||||
fs::directoryListing testListing(source);
|
||||
|
||||
if(testListing.getListingCount() > 0)
|
||||
{
|
||||
// Folder isn't empty, erase
|
||||
fs::eraseSaveData();
|
||||
// Copy source to save container
|
||||
fs::io::file::copyDirectoryCommit(source, fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->journalSize);
|
||||
}
|
||||
}
|
||||
else if(std::filesystem::is_directory(argsIn->source) == false && fileExtension == "zip")
|
||||
{
|
||||
// Open zip for decompressing
|
||||
unzFile unzip = unzOpen64(argsIn->source.c_str());
|
||||
// Check if opening was successful and not empty first
|
||||
if(unzip != NULL && fs::io::zip::zipFileIsNotEmpty(unzip))
|
||||
{
|
||||
fs::io::zip::copyZipToDirectory(unzip, fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->journalSize);
|
||||
}
|
||||
// Close zip
|
||||
unzClose(unzip);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
|
||||
{
|
||||
if(args == nullptr)
|
||||
if (args == nullptr)
|
||||
{
|
||||
task->finished();
|
||||
return;
|
||||
@@ -171,15 +199,18 @@ void deleteBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
|
||||
// Cast args
|
||||
std::shared_ptr<pathArgs> argsIn = std::static_pointer_cast<pathArgs>(args);
|
||||
|
||||
if(std::filesystem::is_directory(argsIn->destination))
|
||||
if (std::filesystem::is_directory(argsIn->destination))
|
||||
{
|
||||
std::filesystem::remove_all(argsIn->destination);
|
||||
// Doesn't have trailing slash
|
||||
std::string target = argsIn->destination + "/";
|
||||
fs::io::file::deleteDirectoryRecursively(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just remove it since it's probably a zip file
|
||||
std::filesystem::remove(argsIn->destination);
|
||||
}
|
||||
|
||||
// Refresh to reflect changes
|
||||
argsIn->sendingState->loadDirectoryList();
|
||||
|
||||
task->finished();
|
||||
@@ -243,7 +274,7 @@ void backupMenuState::update(void)
|
||||
ui::popMessage::newMessage(ui::strings::getString(LANG_POP_SAVE_EMPTY, 0), ui::popMessage::POPMESSAGE_DEFAULT_TICKS);
|
||||
}
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_A) && selected > 0)
|
||||
else if (sys::input::buttonDown(HidNpadButton_A) && selected > 0 && m_SaveListing->getListingCount() > 0)
|
||||
{
|
||||
// Selected item
|
||||
std::string selectedItem = m_BackupListing->getItemAt(selected - 1);
|
||||
@@ -269,13 +300,14 @@ void backupMenuState::update(void)
|
||||
// Setup confirmation arg pointer
|
||||
std::shared_ptr<pathArgs> paths = std::make_shared<pathArgs>();
|
||||
paths->source = m_BackupListing->getFullPathToItemAt(selected - 1); // Need to subtract one to account for 'New' option
|
||||
paths->journalSize = m_CurrentTitleInfo->getJournalSize(static_cast<FsSaveDataType>(m_CurrentUserSaveInfo->getSaveDataInfo().save_data_type)); // Needed to prevent commit errors
|
||||
|
||||
// Create confirmation
|
||||
std::unique_ptr<appState> restoreConfirmationState = std::make_unique<confirmState>(confirmRestore, restoreBackup, paths);
|
||||
// Push it to vector
|
||||
jksv::pushNewState(restoreConfirmationState);
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_X) && selected > 0)
|
||||
else if (sys::input::buttonDown(HidNpadButton_X) && selected > 0)
|
||||
{
|
||||
// Get selected
|
||||
std::string selectedItem = m_BackupListing->getItemAt(selected - 1);
|
||||
|
||||
81
src/appStates/titleOptionState.cpp
Normal file
81
src/appStates/titleOptionState.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "appStates/titleOptionState.hpp"
|
||||
#include "filesystem/filesystem.hpp"
|
||||
#include "system/input.hpp"
|
||||
|
||||
enum
|
||||
{
|
||||
TITLE_INFO,
|
||||
ADD_TO_BLACKLIST,
|
||||
CHANGE_OUTPUT_FOLDER,
|
||||
OPEN_IN_FILE_MODE,
|
||||
DELETE_ALL_BACKUPS,
|
||||
RESET_SAVE_DATA,
|
||||
DELETE_SAVE_DATA,
|
||||
EXTEND_SAVE_DATA,
|
||||
EXPORT_SVI
|
||||
};
|
||||
|
||||
titleOptionState::titleOptionState(data::user *currentUser, data::userSaveInfo *currentUserSaveInfo, data::titleInfo *currentTitleInfo) : m_CurrentUser(currentUser), m_CurrentUserSaveInfo(currentUserSaveInfo), m_CurrentTitleInfo(currentTitleInfo)
|
||||
{
|
||||
// Slide out panel
|
||||
m_SlidePanel = std::make_unique<ui::slidePanel>("titleOptionPanel", 410, ui::PANEL_SIDE_RIGHT);
|
||||
// Menu
|
||||
m_OptionsMenu = std::make_unique<ui::menu>(10, 32, 390, 20, 7);
|
||||
// Setup menu
|
||||
for(int i = 0; i < 9; i++)
|
||||
{
|
||||
m_OptionsMenu->addOpt(ui::strings::getString(LANG_TITLE_OPTIONS_MENU, i));
|
||||
}
|
||||
}
|
||||
|
||||
titleOptionState::~titleOptionState() { }
|
||||
|
||||
void titleOptionState::update(void)
|
||||
{
|
||||
// Update panel and menu
|
||||
m_SlidePanel->update();
|
||||
m_OptionsMenu->update();
|
||||
|
||||
if(sys::input::buttonDown(HidNpadButton_A))
|
||||
{
|
||||
switch(m_OptionsMenu->getSelected())
|
||||
{
|
||||
case RESET_SAVE_DATA:
|
||||
{
|
||||
if(fs::mountSaveData(m_CurrentUserSaveInfo->getSaveDataInfo()))
|
||||
{
|
||||
fs::io::file::deleteDirectoryRecursively(fs::DEFAULT_SAVE_MOUNT_DEVICE);
|
||||
fs::commitSaveData();
|
||||
fs::unmountSaveData();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
ui::popMessage::newMessage("Not implemented yet...", ui::popMessage::POPMESSAGE_DEFAULT_TICKS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_B))
|
||||
{
|
||||
m_SlidePanel->closePanel();
|
||||
}
|
||||
else if(m_SlidePanel->isClosed())
|
||||
{
|
||||
appState::deactivateState();
|
||||
}
|
||||
}
|
||||
|
||||
void titleOptionState::render(void)
|
||||
{
|
||||
// Get panel render target
|
||||
SDL_Texture *panelTarget = m_SlidePanel->getPanelRenderTarget();
|
||||
// Clear it
|
||||
graphics::textureClear(panelTarget, COLOR_SLIDE_PANEL_TARGET);
|
||||
// Render menu to panel
|
||||
m_OptionsMenu->render(panelTarget);
|
||||
// Render panel to framebuffer
|
||||
m_SlidePanel->render();
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <memory>
|
||||
#include "appStates/titleSelectionState.hpp"
|
||||
#include "appStates/backupMenuState.hpp"
|
||||
#include "appStates/titleOptionState.hpp"
|
||||
#include "filesystem/filesystem.hpp"
|
||||
#include "graphics/graphics.hpp"
|
||||
#include "system/input.hpp"
|
||||
@@ -42,6 +44,18 @@ void titleSelectionState::update(void)
|
||||
logger::log("Error mounting save for %016lX", currentUserSaveInfo->getTitleID());
|
||||
}
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_X))
|
||||
{
|
||||
// Get some stuff needed
|
||||
int selected = m_TitleSelection->getSelected();
|
||||
data::userSaveInfo *currentUserSaveInfo = m_CurrentUser->getUserSaveInfoAt(selected);
|
||||
data::titleInfo *currentTitleInfo = data::getTitleInfoByTitleID(currentUserSaveInfo->getTitleID());
|
||||
|
||||
// Create option state
|
||||
std::unique_ptr<appState> optionState = std::make_unique<titleOptionState>(m_CurrentUser, currentUserSaveInfo, currentTitleInfo);
|
||||
// Push it
|
||||
jksv::pushNewState(optionState);
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_B))
|
||||
{
|
||||
appState::deactivateState();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
#include "filesystem/filesystem.hpp"
|
||||
#include "data/data.hpp"
|
||||
#include "graphics/graphics.hpp"
|
||||
#include "stringUtil.hpp"
|
||||
@@ -9,9 +10,13 @@
|
||||
|
||||
data::titleInfo::titleInfo(const uint64_t &titleID) : m_TitleID(titleID)
|
||||
{
|
||||
// This is the size of the control data. Used for getting icon size
|
||||
uint64_t nsAppControlSize = 0;
|
||||
// Icon size
|
||||
size_t appIconSize = 0;
|
||||
// Actual control data.
|
||||
std::unique_ptr<NsApplicationControlData> nsAppConrolData = std::make_unique<NsApplicationControlData>();
|
||||
// Title ID in hex format
|
||||
std::string titleIDHexString = stringUtil::getFormattedString("%016lX", m_TitleID);
|
||||
|
||||
Result nsAppControlRes = nsGetApplicationControlData(NsApplicationControlSource_Storage, m_TitleID, nsAppConrolData.get(), sizeof(NsApplicationControlData), &nsAppControlSize);
|
||||
@@ -38,18 +43,23 @@ data::titleInfo::titleInfo(const uint64_t &titleID) : m_TitleID(titleID)
|
||||
|
||||
std::string data::titleInfo::getTitle(void)
|
||||
{
|
||||
// Try to get title from NACP
|
||||
NacpLanguageEntry *entry;
|
||||
nacpGetLanguageEntry(&m_Nacp, &entry);
|
||||
// If it fails, return title ID as hex string
|
||||
if(entry == NULL)
|
||||
{
|
||||
return stringUtil::getFormattedString("%016lX", m_TitleID);
|
||||
}
|
||||
// Return the title
|
||||
return std::string(entry->name);
|
||||
}
|
||||
|
||||
std::string data::titleInfo::getPathSafeTitle(void)
|
||||
{
|
||||
// Try to get a pathSafeTitle
|
||||
std::string pathSafeTitle = stringUtil::getPathSafeString(data::titleInfo::getTitle());
|
||||
// If that fails, it will be empty. Just return ID hex string
|
||||
if(pathSafeTitle.empty())
|
||||
{
|
||||
return stringUtil::getFormattedString("%016lX", m_TitleID);
|
||||
@@ -60,4 +70,42 @@ std::string data::titleInfo::getPathSafeTitle(void)
|
||||
SDL_Texture *data::titleInfo::getIcon(void)
|
||||
{
|
||||
return m_Icon;
|
||||
}
|
||||
|
||||
uint64_t data::titleInfo::getJournalSize(const FsSaveDataType &saveType)
|
||||
{
|
||||
// Journal size to return
|
||||
uint64_t journalSize = 0;
|
||||
|
||||
// Different save types have different journal limits
|
||||
switch(saveType)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
{
|
||||
// Just this is safe for accounts
|
||||
journalSize = m_Nacp.user_account_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Bcat:
|
||||
{
|
||||
// Let's just hope this works all the time
|
||||
journalSize = m_Nacp.bcat_delivery_cache_storage_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Device:
|
||||
{
|
||||
journalSize = m_Nacp.device_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// This should make it commit on every write. Not the fastest, but safest.
|
||||
journalSize = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return journalSize;
|
||||
}
|
||||
@@ -101,4 +101,10 @@ void fs::unmountSaveData(void)
|
||||
void fs::commitSaveData(void)
|
||||
{
|
||||
fsdevCommitDevice(DEFAULT_SAVE_MOUNT_DEVICE);
|
||||
}
|
||||
|
||||
void fs::eraseSaveData(void)
|
||||
{
|
||||
fs::io::file::deleteDirectoryRecursively(fs::DEFAULT_SAVE_MOUNT_DEVICE);
|
||||
fs::commitSaveData();
|
||||
}
|
||||
@@ -11,20 +11,20 @@
|
||||
#include "log.hpp"
|
||||
|
||||
// Read thread function
|
||||
void fs::io::readThreadFunction(const std::string &source, std::shared_ptr<threadStruct> sharedStruct)
|
||||
void fs::io::file::readThreadFunction(const std::string &source, std::shared_ptr<threadStruct> sharedStruct)
|
||||
{
|
||||
// Bytes read
|
||||
uint64_t bytesRead = 0;
|
||||
// File stream
|
||||
std::ifstream sourceFile(source, std::ios::binary);
|
||||
// Local buffer for reading
|
||||
std::vector<char> localBuffer(fs::io::FILE_BUFFER_SIZE);
|
||||
std::vector<char> localBuffer(fs::io::file::FILE_BUFFER_SIZE);
|
||||
|
||||
// Loop until file is fully read
|
||||
while(bytesRead < sharedStruct->fileSize)
|
||||
{
|
||||
// Read to localBuffer
|
||||
sourceFile.read(localBuffer.data(), fs::io::FILE_BUFFER_SIZE);
|
||||
sourceFile.read(localBuffer.data(), fs::io::file::FILE_BUFFER_SIZE);
|
||||
|
||||
// Wait for shared buffer to be emptied by write thread
|
||||
std::unique_lock sharedBufferLock(sharedStruct->bufferLock);
|
||||
@@ -48,7 +48,7 @@ void fs::io::readThreadFunction(const std::string &source, std::shared_ptr<threa
|
||||
}
|
||||
|
||||
// File writing thread function
|
||||
void fs::io::writeThreadFunction(const std::string &destination, std::shared_ptr<threadStruct> sharedStruct)
|
||||
void fs::io::file::writeThreadFunction(const std::string &destination, std::shared_ptr<threadStruct> sharedStruct)
|
||||
{
|
||||
// Keep track of bytes written
|
||||
uint64_t bytesWritten = 0;
|
||||
@@ -105,50 +105,50 @@ void fs::io::writeThreadFunction(const std::string &destination, std::shared_ptr
|
||||
fs::commitSaveData();
|
||||
}
|
||||
|
||||
int fs::io::getFileSize(const std::string &filePath)
|
||||
int fs::io::file::getFileSize(const std::string &filePath)
|
||||
{
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
file.seekg(std::ios::end);
|
||||
return file.tellg();
|
||||
}
|
||||
|
||||
void fs::io::copyFile(const std::string &source, const std::string &destination)
|
||||
void fs::io::file::copyFile(const std::string &source, const std::string &destination)
|
||||
{
|
||||
// Create shared thread struct
|
||||
std::shared_ptr<threadStruct> sharedStruct = std::make_shared<threadStruct>();
|
||||
|
||||
// Everything else is set by default
|
||||
sharedStruct->fileSize = fs::io::getFileSize(source);
|
||||
sharedStruct->fileSize = fs::io::file::getFileSize(source);
|
||||
|
||||
// Read & write thread
|
||||
std::thread readThread(fs::io::readThreadFunction, source, sharedStruct);
|
||||
std::thread writeThread(fs::io::writeThreadFunction, destination, sharedStruct);
|
||||
std::thread readThread(fs::io::file::readThreadFunction, source, sharedStruct);
|
||||
std::thread writeThread(fs::io::file::writeThreadFunction, destination, sharedStruct);
|
||||
|
||||
// Wait for finish
|
||||
readThread.join();
|
||||
writeThread.join();
|
||||
}
|
||||
|
||||
void fs::io::copyFileCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize)
|
||||
void fs::io::file::copyFileCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize)
|
||||
{
|
||||
// Shared struct
|
||||
std::shared_ptr<threadStruct> sharedStruct = std::make_shared<threadStruct>();
|
||||
|
||||
// Set vars
|
||||
sharedStruct->fileSize = fs::io::getFileSize(source);
|
||||
sharedStruct->fileSize = fs::io::file::getFileSize(source);
|
||||
sharedStruct->commitWrite = true;
|
||||
sharedStruct->journalSize = journalSize;
|
||||
|
||||
// Threads
|
||||
std::thread readThread(fs::io::readThreadFunction, source, sharedStruct);
|
||||
std::thread writeThread(fs::io::writeThreadFunction, destination, sharedStruct);
|
||||
std::thread readThread(fs::io::file::readThreadFunction, source, sharedStruct);
|
||||
std::thread writeThread(fs::io::file::writeThreadFunction, destination, sharedStruct);
|
||||
|
||||
// Wait
|
||||
readThread.join();
|
||||
writeThread.join();
|
||||
}
|
||||
|
||||
void fs::io::copyDirectory(const std::string &source, const std::string &destination)
|
||||
void fs::io::file::copyDirectory(const std::string &source, const std::string &destination)
|
||||
{
|
||||
fs::directoryListing list(source);
|
||||
|
||||
@@ -160,18 +160,18 @@ void fs::io::copyDirectory(const std::string &source, const std::string &destina
|
||||
std::string newSource = source + list.getItemAt(i) + "/";
|
||||
std::string newDestination = destination + list.getItemAt(i) + "/";
|
||||
std::filesystem::create_directories(newDestination);
|
||||
fs::io::copyDirectory(newSource, newDestination);
|
||||
fs::io::file::copyDirectory(newSource, newDestination);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string fullSource = source + list.getItemAt(i);
|
||||
std::string fullDestination = destination + list.getItemAt(i);
|
||||
fs::io::copyFile(fullSource, fullDestination);
|
||||
fs::io::file::copyFile(fullSource, fullDestination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fs::io::copyDirectoryCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize)
|
||||
void fs::io::file::copyDirectoryCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize)
|
||||
{
|
||||
fs::directoryListing list(source);
|
||||
|
||||
@@ -183,13 +183,41 @@ void fs::io::copyDirectoryCommit(const std::string &source, const std::string &d
|
||||
std::string newSource = source + list.getItemAt(i) + "/";
|
||||
std::string newDestination = destination + list.getItemAt(i) + "/";
|
||||
std::filesystem::create_directory(newDestination.substr(0, newDestination.npos - 1));
|
||||
fs::io::copyDirectoryCommit(newSource, newDestination, journalSize);
|
||||
fs::io::file::copyDirectoryCommit(newSource, newDestination, journalSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string fullSource = source + list.getItemAt(i);
|
||||
std::string fullDestination = destination + list.getItemAt(i);
|
||||
fs::io::copyFileCommit(fullSource, fullDestination, journalSize);
|
||||
fs::io::file::copyFileCommit(fullSource, fullDestination, journalSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fs::io::file::deleteDirectoryRecursively(const std::string &target)
|
||||
{
|
||||
// Get directory listing
|
||||
fs::directoryListing listing(target);
|
||||
// Get item count
|
||||
int listingCount = listing.getListingCount();
|
||||
// Loop through
|
||||
for(int i = 0; i < listingCount; i++)
|
||||
{
|
||||
if(listing.itemAtIsDirectory(i))
|
||||
{
|
||||
// New target path
|
||||
std::string newTarget = listing.getFullPathToItemAt(i) + "/";
|
||||
// Call self to continue
|
||||
fs::io::file::deleteDirectoryRecursively(newTarget);
|
||||
// Delete directory
|
||||
std::filesystem::remove(listing.getFullPathToItemAt(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just delete file
|
||||
std::filesystem::remove(listing.getFullPathToItemAt(i));
|
||||
}
|
||||
}
|
||||
// Finally delete last directory
|
||||
std::filesystem::remove(target);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
@@ -11,7 +12,7 @@
|
||||
|
||||
#define ZIP_BUFFER_SIZE 0x80000
|
||||
|
||||
void fs::io::copyDirectoryToZip(const std::string &source, zipFile zip)
|
||||
void fs::io::zip::copyDirectoryToZip(const std::string &source, zipFile zip)
|
||||
{
|
||||
// Source file listing
|
||||
fs::directoryListing listing(source);
|
||||
@@ -23,7 +24,7 @@ void fs::io::copyDirectoryToZip(const std::string &source, zipFile zip)
|
||||
if(listing.itemAtIsDirectory(i))
|
||||
{
|
||||
std::string newSource = source + listing.getItemAt(i) + "/";
|
||||
fs::io::copyDirectoryToZip(newSource, zip);
|
||||
fs::io::zip::copyDirectoryToZip(newSource, zip);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -86,12 +87,12 @@ void fs::io::copyDirectoryToZip(const std::string &source, zipFile zip)
|
||||
}
|
||||
}
|
||||
|
||||
void fs::io::copyZipToDirectory(unzFile unzip, const std::string &destination, const uint64_t &journalSize)
|
||||
void fs::io::zip::copyZipToDirectory(unzFile unzip, const std::string &destination, const uint64_t &journalSize)
|
||||
{
|
||||
// Buffer to decompress to
|
||||
std::vector<char> buffer(ZIP_BUFFER_SIZE);
|
||||
// Filename of file being decompressed
|
||||
char currentFileName[FS_MAX_PATH];
|
||||
std::array<char, FS_MAX_PATH> currentFileName;
|
||||
// This contains CRC etc. I don't use it but it's needed.
|
||||
unz_file_info64 unzipFileInfo;
|
||||
// Keep track of journaling space commits
|
||||
@@ -100,11 +101,11 @@ void fs::io::copyZipToDirectory(unzFile unzip, const std::string &destination, c
|
||||
uint32_t bytesRead = 0;
|
||||
do
|
||||
{
|
||||
unzGetCurrentFileInfo64(unzip, &unzipFileInfo, currentFileName, FS_MAX_PATH, NULL, 0, NULL, 0);
|
||||
unzGetCurrentFileInfo64(unzip, &unzipFileInfo, currentFileName.data(), FS_MAX_PATH, NULL, 0, NULL, 0);
|
||||
if(unzOpenCurrentFile(unzip) == UNZ_OK)
|
||||
{
|
||||
// Full path to output
|
||||
std::string fullDestination = destination + currentFileName;
|
||||
std::string fullDestination = destination + currentFileName.data();
|
||||
// Make sure full path to exists
|
||||
std::filesystem::create_directories(fullDestination.substr(0, fullDestination.find_last_of('/') + 1));
|
||||
// File stream
|
||||
@@ -135,3 +136,32 @@ void fs::io::copyZipToDirectory(unzFile unzip, const std::string &destination, c
|
||||
fs::commitSaveData();
|
||||
}
|
||||
|
||||
uint64_t fs::io::zip::getZipTotalFileSize(unzFile unzip)
|
||||
{
|
||||
// Total size to return
|
||||
uint64_t totalSize = 0;
|
||||
if(unzGoToFirstFile(unzip) == UNZ_OK)
|
||||
{
|
||||
// Needed to get size
|
||||
unz_file_info64 fileInfo = { 0 };
|
||||
std::array<char, FS_MAX_PATH> filename;
|
||||
|
||||
// Loop through zip file
|
||||
do
|
||||
{
|
||||
// Get info
|
||||
unzGetCurrentFileInfo64(unzip, &fileInfo, filename.data(), FS_MAX_PATH, NULL, 0, NULL, 0);
|
||||
// Add size to total size
|
||||
totalSize += fileInfo.uncompressed_size;
|
||||
} while (unzGoToNextFile(unzip) != UNZ_END_OF_LIST_OF_FILE);
|
||||
// Reset to beginning of zip
|
||||
unzGoToFirstFile(unzip);
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
bool fs::io::zip::zipFileIsNotEmpty(unzFile unzip)
|
||||
{
|
||||
// Just return if we can get first file
|
||||
return unzGoToFirstFile(unzip) == UNZ_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user