Work on fixing C++ filesystem issues

This commit is contained in:
JK 2024-05-30 09:39:00 -04:00
parent 1aca1aa33e
commit f30faad231
10 changed files with 190 additions and 99 deletions

View File

@ -9,47 +9,44 @@ namespace fs
{
namespace io
{
namespace file
// 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
{
// 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;
// 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);
// 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);
}
// 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

@ -4,21 +4,17 @@
#include <minizip/zip.h>
#include <minizip/unzip.h>
namespace fs
{
namespace io
namespace zip
{
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);
}
// 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

@ -1,6 +1,8 @@
#pragma once
#include <string>
/* HUGE TO DO: REVISE THIS ENTIRE NAMING THING*/
// To prevent typos
#define LANG_AUTHOR "author"
#define LANG_USER_GUIDE "helpUser"
@ -39,6 +41,7 @@
#define LANG_SAVEDATA_DELETE_ALL_USER_SAVES "saveDataDeleteAllUser"
#define LANG_SAVEDATA_BACKUP_DELETED "saveDataBackupDeleted"
#define LANG_SAVEDATA_BACKUP_TRASHED "saveDataBackupMovedToTrash"
#define LANG_SAVEDATA_ERROR_MOUNTING "saveDataErrorMounting"
// Save data types
#define LANG_SAVEDATA_TYPE_TEXT "saveDataTypeText"

View File

@ -81,13 +81,13 @@ 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::zip::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, zipOut);
fs::zip::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, zipOut);
zipClose(zipOut, "");
}
else
{
std::filesystem::create_directories(path);
fs::io::file::copyDirectory(fs::DEFAULT_SAVE_MOUNT_DEVICE, path);
fs::io::copyDirectory(fs::DEFAULT_SAVE_MOUNT_DEVICE, path);
}
}
@ -120,11 +120,11 @@ void overwriteBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
// 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);
fs::io::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);
fs::io::copyDirectory(fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->destination);
}
else if (std::filesystem::is_directory(argsIn->destination) == false && fileExtension == "zip")
{
@ -133,7 +133,7 @@ void overwriteBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
// 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);
fs::zip::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, zipOut);
zipClose(zipOut, "");
}
task->finished();
@ -168,7 +168,7 @@ void restoreBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
// Folder isn't empty, erase
fs::eraseSaveData();
// Copy source to save container
fs::io::file::copyDirectoryCommit(source, fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->journalSize);
fs::io::copyDirectoryCommit(source, fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->journalSize);
}
}
else if(std::filesystem::is_directory(argsIn->source) == false && fileExtension == "zip")
@ -176,9 +176,9 @@ void restoreBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
// 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))
if(unzip != NULL && fs::zip::zipFileIsNotEmpty(unzip))
{
fs::io::zip::copyZipToDirectory(unzip, fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->journalSize);
fs::zip::copyZipToDirectory(unzip, fs::DEFAULT_SAVE_MOUNT_DEVICE, argsIn->journalSize);
}
// Close zip
unzClose(unzip);
@ -203,7 +203,7 @@ void deleteBackup(sys::task *task, std::shared_ptr<sys::taskArgs> args)
{
// Doesn't have trailing slash
std::string target = argsIn->destination + "/";
fs::io::file::deleteDirectoryRecursively(target);
fs::io::deleteDirectoryRecursively(target);
}
else
{

View File

@ -1,6 +1,13 @@
#include <memory>
#include "appStates/titleOptionState.hpp"
#include "appStates/confirmState.hpp"
#include "data/data.hpp"
#include "filesystem/filesystem.hpp"
#include "ui/ui.hpp"
#include "system/task.hpp"
#include "system/input.hpp"
#include "jksv.hpp"
#include "log.hpp"
enum
{
@ -15,6 +22,55 @@ enum
EXPORT_SVI
};
// This struct is used to send data to tasks
struct titleOptsArgs : sys::taskArgs
{
data::user *currentUser;
data::userSaveInfo *currentUserSaveInfo;
data::titleInfo *currentTitleInfo;
};
// These are the task/thread functions for this state
// This was needed to debug something. Only one for now.
void resetSaveData(sys::task *task, std::shared_ptr<sys::taskArgs> args)
{
if(args == nullptr)
{
task->finished();
return;
}
logger::log("Arg check");
// Set status
task->setThreadStatus(ui::strings::getString(LANG_THREAD_RESET_SAVE_DATA, 0));
logger::log("Set status");
// Cast args
std::shared_ptr<titleOptsArgs> argsIn = std::static_pointer_cast<titleOptsArgs>(args);
logger::log("Cast pointer");
if(fs::mountSaveData(argsIn->currentUserSaveInfo->getSaveDataInfo()))
{
logger::log("Save mounted");
// This does everything this needs
fs::eraseSaveData();
logger::log("Erase Save Data");
// Close
fs::unmountSaveData();
logger::log("unmount");
// Display success
ui::popMessage::newMessage(ui::strings::getString(LANG_SAVEDATA_RESET_SUCCESS, 0), ui::popMessage::POPMESSAGE_DEFAULT_TICKS);
logger::log("POP");
}
else
{
// Display failure
ui::popMessage::newMessage(ui::strings::getString(LANG_SAVEDATA_ERROR_MOUNTING, 0), ui::popMessage::POPMESSAGE_DEFAULT_TICKS);
}
task->finished();
}
titleOptionState::titleOptionState(data::user *currentUser, data::userSaveInfo *currentUserSaveInfo, data::titleInfo *currentTitleInfo) : m_CurrentUser(currentUser), m_CurrentUserSaveInfo(currentUserSaveInfo), m_CurrentTitleInfo(currentTitleInfo)
{
// Slide out panel
@ -40,14 +96,21 @@ void titleOptionState::update(void)
{
switch(m_OptionsMenu->getSelected())
{
// This is needed to debug something... and itself.
case RESET_SAVE_DATA:
{
if(fs::mountSaveData(m_CurrentUserSaveInfo->getSaveDataInfo()))
{
fs::io::file::deleteDirectoryRecursively(fs::DEFAULT_SAVE_MOUNT_DEVICE);
fs::commitSaveData();
fs::unmountSaveData();
}
// Prepare confirmation
std::string confirmationString = ui::strings::getString(LANG_CONFIRM_RESET_SAVEDATA, 0);
// Data to send
std::shared_ptr<titleOptsArgs> args = std::make_shared<titleOptsArgs>();
args->currentUserSaveInfo = m_CurrentUserSaveInfo;
// Confirm state
std::unique_ptr<appState> confirmReset = std::make_unique<confirmState>(confirmationString, resetSaveData, args);
// Push
jksv::pushNewState(confirmReset);
}
break;

View File

@ -14,7 +14,7 @@ void fs::directoryListing::loadListing(void)
// Clear vector first JIC reload
m_DirectoryList.clear();
for(const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator{m_DirectoryPath})
for(const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(m_DirectoryPath))
{
m_DirectoryList.push_back(entry.path().string());
}

View File

@ -105,6 +105,8 @@ void fs::commitSaveData(void)
void fs::eraseSaveData(void)
{
fs::io::file::deleteDirectoryRecursively(fs::DEFAULT_SAVE_MOUNT_DEVICE);
// Delete root of save container
fs::io::deleteDirectoryRecursively(fs::DEFAULT_SAVE_MOUNT_DEVICE);
// Commit changes
fs::commitSaveData();
}

View File

@ -11,20 +11,20 @@
#include "log.hpp"
// Read thread function
void fs::io::file::readThreadFunction(const std::string &source, std::shared_ptr<threadStruct> sharedStruct)
void fs::io::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::FILE_BUFFER_SIZE);
std::vector<char> localBuffer(fs::io::FILE_BUFFER_SIZE);
// Loop until file is fully read
while(bytesRead < sharedStruct->fileSize)
{
// Read to localBuffer
sourceFile.read(localBuffer.data(), fs::io::file::FILE_BUFFER_SIZE);
sourceFile.read(localBuffer.data(), fs::io::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::file::readThreadFunction(const std::string &source, std::shared_ptr
}
// File writing thread function
void fs::io::file::writeThreadFunction(const std::string &destination, std::shared_ptr<threadStruct> sharedStruct)
void fs::io::writeThreadFunction(const std::string &destination, std::shared_ptr<threadStruct> sharedStruct)
{
// Keep track of bytes written
uint64_t bytesWritten = 0;
@ -105,101 +105,127 @@ void fs::io::file::writeThreadFunction(const std::string &destination, std::shar
fs::commitSaveData();
}
int fs::io::file::getFileSize(const std::string &filePath)
int fs::io::getFileSize(const std::string &filePath)
{
// Open file for reading to prevent problems
std::ifstream file(filePath, std::ios::binary);
// Seek to end
file.seekg(std::ios::end);
// Return offset. ifstream destructor should take care of closing
return file.tellg();
}
void fs::io::file::copyFile(const std::string &source, const std::string &destination)
void fs::io::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::file::getFileSize(source);
sharedStruct->fileSize = fs::io::getFileSize(source);
// Read & write thread
std::thread readThread(fs::io::file::readThreadFunction, source, sharedStruct);
std::thread writeThread(fs::io::file::writeThreadFunction, destination, sharedStruct);
std::thread readThread(fs::io::readThreadFunction, source, sharedStruct);
std::thread writeThread(fs::io::writeThreadFunction, destination, sharedStruct);
// Wait for finish
readThread.join();
writeThread.join();
}
void fs::io::file::copyFileCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize)
void fs::io::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::file::getFileSize(source);
sharedStruct->fileSize = fs::io::getFileSize(source);
sharedStruct->commitWrite = true;
sharedStruct->journalSize = journalSize;
// Threads
std::thread readThread(fs::io::file::readThreadFunction, source, sharedStruct);
std::thread writeThread(fs::io::file::writeThreadFunction, destination, sharedStruct);
std::thread readThread(fs::io::readThreadFunction, source, sharedStruct);
std::thread writeThread(fs::io::writeThreadFunction, destination, sharedStruct);
// Wait
readThread.join();
writeThread.join();
}
void fs::io::file::copyDirectory(const std::string &source, const std::string &destination)
void fs::io::copyDirectory(const std::string &source, const std::string &destination)
{
// Get source directory listing
fs::directoryListing list(source);
// Get count and loop through list
int listCount = list.getListingCount();
for(int i = 0; i < listCount; i++)
{
if(list.itemAtIsDirectory(i))
{
// New source and destination directories
std::string newSource = source + list.getItemAt(i) + "/";
std::string newDestination = destination + list.getItemAt(i) + "/";
// Create all of the directories in the path JIC
std::filesystem::create_directories(newDestination);
fs::io::file::copyDirectory(newSource, newDestination);
// Recusive copy using new source and destination
fs::io::copyDirectory(newSource, newDestination);
}
else
{
// Full source and destination directories
std::string fullSource = source + list.getItemAt(i);
std::string fullDestination = destination + list.getItemAt(i);
fs::io::file::copyFile(fullSource, fullDestination);
// Copy file
fs::io::copyFile(fullSource, fullDestination);
}
}
}
void fs::io::file::copyDirectoryCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize)
void fs::io::copyDirectoryCommit(const std::string &source, const std::string &destination, const uint64_t &journalSize)
{
// Source listing
fs::directoryListing list(source);
// Loop through it
int listCount = list.getListingCount();
for(int i = 0; i < listCount; i++)
{
if(list.itemAtIsDirectory(i))
{
// New source and destination with item appended to end with trailing slash
std::string newSource = source + list.getItemAt(i) + "/";
std::string newDestination = destination + list.getItemAt(i) + "/";
// Create new directory in destination
std::filesystem::create_directory(newDestination.substr(0, newDestination.npos - 1));
fs::io::file::copyDirectoryCommit(newSource, newDestination, journalSize);
// Recursive
fs::io::copyDirectoryCommit(newSource, newDestination, journalSize);
}
else
{
// Full path to source and destination files
std::string fullSource = source + list.getItemAt(i);
std::string fullDestination = destination + list.getItemAt(i);
fs::io::file::copyFileCommit(fullSource, fullDestination, journalSize);
// Copy it and commit it to save
fs::io::copyFileCommit(fullSource, fullDestination, journalSize);
}
}
}
void fs::io::file::deleteDirectoryRecursively(const std::string &target)
void fs::io::deleteDirectoryRecursively(const std::string &target)
{
// Get directory listing
fs::directoryListing listing(target);
// Get item count
int listingCount = listing.getListingCount();
// Error code to stop from throwing exceptions on failure to delete.
std::error_code errorCode;
// Loop through
for(int i = 0; i < listingCount; i++)
{
@ -208,16 +234,16 @@ void fs::io::file::deleteDirectoryRecursively(const std::string &target)
// New target path
std::string newTarget = listing.getFullPathToItemAt(i) + "/";
// Call self to continue
fs::io::file::deleteDirectoryRecursively(newTarget);
fs::io::deleteDirectoryRecursively(newTarget);
// Delete directory
std::filesystem::remove(listing.getFullPathToItemAt(i));
std::filesystem::remove(listing.getFullPathToItemAt(i), errorCode);
}
else
{
// Just delete file
std::filesystem::remove(listing.getFullPathToItemAt(i));
std::filesystem::remove(listing.getFullPathToItemAt(i), errorCode);
}
}
// Finally delete last directory
std::filesystem::remove(target);
std::filesystem::remove(target, errorCode);
}

View File

@ -12,7 +12,7 @@
#define ZIP_BUFFER_SIZE 0x80000
void fs::io::zip::copyDirectoryToZip(const std::string &source, zipFile zip)
void fs::zip::copyDirectoryToZip(const std::string &source, zipFile zip)
{
// Source file listing
fs::directoryListing listing(source);
@ -24,7 +24,7 @@ void fs::io::zip::copyDirectoryToZip(const std::string &source, zipFile zip)
if(listing.itemAtIsDirectory(i))
{
std::string newSource = source + listing.getItemAt(i) + "/";
fs::io::zip::copyDirectoryToZip(newSource, zip);
fs::zip::copyDirectoryToZip(newSource, zip);
}
else
{
@ -87,7 +87,7 @@ void fs::io::zip::copyDirectoryToZip(const std::string &source, zipFile zip)
}
}
void fs::io::zip::copyZipToDirectory(unzFile unzip, const std::string &destination, const uint64_t &journalSize)
void fs::zip::copyZipToDirectory(unzFile unzip, const std::string &destination, const uint64_t &journalSize)
{
// Buffer to decompress to
std::vector<char> buffer(ZIP_BUFFER_SIZE);
@ -136,7 +136,7 @@ void fs::io::zip::copyZipToDirectory(unzFile unzip, const std::string &destinati
fs::commitSaveData();
}
uint64_t fs::io::zip::getZipTotalFileSize(unzFile unzip)
uint64_t fs::zip::getZipTotalFileSize(unzFile unzip)
{
// Total size to return
uint64_t totalSize = 0;
@ -160,7 +160,7 @@ uint64_t fs::io::zip::getZipTotalFileSize(unzFile unzip)
return totalSize;
}
bool fs::io::zip::zipFileIsNotEmpty(unzFile unzip)
bool fs::zip::zipFileIsNotEmpty(unzFile unzip)
{
// Just return if we can get first file
return unzGoToFirstFile(unzip) == UNZ_OK;

View File

@ -78,12 +78,15 @@ void ui::strings::init(void)
addUIString("saveDataDeleteAllUser", 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*");
addUIString("saveDataBackupDeleted", 0, "#%s# has been deleted.");
addUIString("saveDataBackupMovedToTrash", 0, "#%s# has been moved to trash.");
addUIString("saveDataErrorMounting", 0, "Error mounting save data!");
addUIString("saveTypeMainMenu", 0, "Device");
addUIString("saveTypeMainMenu", 1, "BCAT");
addUIString("saveTypeMainMenu", 2, "Cache");
addUIString("saveTypeMainMenu", 3, "System");
addUIString("saveTypeMainMenu", 4, "System BCAT");
addUIString("saveTypeMainMenu", 5, "Temporary");
// This is redundant. Need to merge and use one or the other...
addUIString("saveDataTypeText", 0, "System");
addUIString("saveDataTypeText", 1, "Account");
@ -221,6 +224,7 @@ void ui::strings::init(void)
addUIString("popDriveStarted", 0, "Google Drive started successfully.");
addUIString("popDriveFailed", 0, "Failed to start Google Drive.");
addUIString("popDriveNotActive", 0, "Google Drive is not available");
addUIString("popErrorMountingSave", 0, "Error mounting save data!");
// Keyboard hints
addUIString("swkbdEnterName", 0, "Enter a new name");