From f30faad231fab03c70b0fed03e60dc8c1cfbbfcf Mon Sep 17 00:00:00 2001 From: JK Date: Thu, 30 May 2024 09:39:00 -0400 Subject: [PATCH] Work on fixing C++ filesystem issues --- inc/filesystem/io.hpp | 77 ++++++++++++++--------------- inc/filesystem/zip.hpp | 22 ++++----- inc/ui/strings.hpp | 3 ++ src/appStates/backupMenuState.cpp | 18 +++---- src/appStates/titleOptionState.cpp | 75 +++++++++++++++++++++++++--- src/filesystem/directoryListing.cpp | 2 +- src/filesystem/filesystem.cpp | 4 +- src/filesystem/io.cpp | 74 ++++++++++++++++++--------- src/filesystem/zip.cpp | 10 ++-- src/ui/strings.cpp | 4 ++ 10 files changed, 190 insertions(+), 99 deletions(-) diff --git a/inc/filesystem/io.hpp b/inc/filesystem/io.hpp index cf267fd..b3a337c 100644 --- a/inc/filesystem/io.hpp +++ b/inc/filesystem/io.hpp @@ -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 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 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 sharedStruct); - void writeThreadFunction(const std::string &destination, std::shared_ptr 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 sharedStruct); + void writeThreadFunction(const std::string &destination, std::shared_ptr sharedStruct); } } \ No newline at end of file diff --git a/inc/filesystem/zip.hpp b/inc/filesystem/zip.hpp index 5fd01cd..fc68810 100644 --- a/inc/filesystem/zip.hpp +++ b/inc/filesystem/zip.hpp @@ -4,21 +4,17 @@ #include #include - 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); } } \ No newline at end of file diff --git a/inc/ui/strings.hpp b/inc/ui/strings.hpp index f4086f2..8be4bcd 100644 --- a/inc/ui/strings.hpp +++ b/inc/ui/strings.hpp @@ -1,6 +1,8 @@ #pragma once #include +/* 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" diff --git a/src/appStates/backupMenuState.cpp b/src/appStates/backupMenuState.cpp index 5d09520..71edffa 100644 --- a/src/appStates/backupMenuState.cpp +++ b/src/appStates/backupMenuState.cpp @@ -81,13 +81,13 @@ void createNewBackup(sys::task *task, std::shared_ptr 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 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 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 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 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 args) { // Doesn't have trailing slash std::string target = argsIn->destination + "/"; - fs::io::file::deleteDirectoryRecursively(target); + fs::io::deleteDirectoryRecursively(target); } else { diff --git a/src/appStates/titleOptionState.cpp b/src/appStates/titleOptionState.cpp index 92b4ea5..a946076 100644 --- a/src/appStates/titleOptionState.cpp +++ b/src/appStates/titleOptionState.cpp @@ -1,6 +1,13 @@ +#include #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 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 argsIn = std::static_pointer_cast(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 args = std::make_shared(); + args->currentUserSaveInfo = m_CurrentUserSaveInfo; + + // Confirm state + std::unique_ptr confirmReset = std::make_unique(confirmationString, resetSaveData, args); + + // Push + jksv::pushNewState(confirmReset); } break; diff --git a/src/filesystem/directoryListing.cpp b/src/filesystem/directoryListing.cpp index c4e789e..659c849 100644 --- a/src/filesystem/directoryListing.cpp +++ b/src/filesystem/directoryListing.cpp @@ -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()); } diff --git a/src/filesystem/filesystem.cpp b/src/filesystem/filesystem.cpp index 0fe3cf1..a21def5 100644 --- a/src/filesystem/filesystem.cpp +++ b/src/filesystem/filesystem.cpp @@ -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(); } \ No newline at end of file diff --git a/src/filesystem/io.cpp b/src/filesystem/io.cpp index 8319c74..92e7adc 100644 --- a/src/filesystem/io.cpp +++ b/src/filesystem/io.cpp @@ -11,20 +11,20 @@ #include "log.hpp" // Read thread function -void fs::io::file::readThreadFunction(const std::string &source, std::shared_ptr sharedStruct) +void fs::io::readThreadFunction(const std::string &source, std::shared_ptr sharedStruct) { // Bytes read uint64_t bytesRead = 0; // File stream std::ifstream sourceFile(source, std::ios::binary); // Local buffer for reading - std::vector localBuffer(fs::io::file::FILE_BUFFER_SIZE); + std::vector 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 sharedStruct) +void fs::io::writeThreadFunction(const std::string &destination, std::shared_ptr 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 sharedStruct = std::make_shared(); // 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 sharedStruct = std::make_shared(); // 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); } \ No newline at end of file diff --git a/src/filesystem/zip.cpp b/src/filesystem/zip.cpp index 59ffec1..c277708 100644 --- a/src/filesystem/zip.cpp +++ b/src/filesystem/zip.cpp @@ -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 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; diff --git a/src/ui/strings.cpp b/src/ui/strings.cpp index 9d63fe3..aa01b20 100644 --- a/src/ui/strings.cpp +++ b/src/ui/strings.cpp @@ -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");