Nothing major

This commit is contained in:
Your Name
2024-05-30 09:31:49 -04:00
parent 9cf236d7fe
commit 1aca1aa33e
13 changed files with 381 additions and 97 deletions

View 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;
};

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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"

View File

@@ -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);

View 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();
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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;
}