diff --git a/include/fs/ScopedSaveMount.hpp b/include/fs/ScopedSaveMount.hpp new file mode 100644 index 0000000..9060b60 --- /dev/null +++ b/include/fs/ScopedSaveMount.hpp @@ -0,0 +1,34 @@ +#pragma once +#include +#include + +namespace fs +{ + class ScopedSaveMount + { + public: + /// @brief Opens a scope save mount using the FsSaveDataInfo passed. + /// @param mount Mount point. + /// @param info Save info to mount. + ScopedSaveMount(std::string_view mount, const FsSaveDataInfo *saveInfo); + + ScopedSaveMount(ScopedSaveMount &&scopedSaveMount); + ScopedSaveMount &operator=(ScopedSaveMount &&scopedSaveMount); + + ScopedSaveMount(const ScopedSaveMount &) = delete; + ScopedSaveMount &operator=(const ScopedSaveMount &) = delete; + + /// @brief Closes the save mounted. + ~ScopedSaveMount(); + + /// @brief Returns whether or not mounting the data was successful. + bool is_open() const; + + private: + /// @brief Saves a copy of the mount point for destruction. + std::string m_mountPoint{}; + + /// @brief Stores whether or not mounting the save was successful. + bool m_isOpen{}; + }; +} diff --git a/include/fs/fs.hpp b/include/fs/fs.hpp index ffda502..7a4c099 100644 --- a/include/fs/fs.hpp +++ b/include/fs/fs.hpp @@ -2,6 +2,7 @@ #include "fs/MiniUnzip.hpp" #include "fs/MiniZip.hpp" #include "fs/SaveMetaData.hpp" +#include "fs/ScopedSaveMount.hpp" #include "fs/directory_functions.hpp" #include "fs/io.hpp" #include "fs/save_data_functions.hpp" diff --git a/romfs/Text/ENUS.json b/romfs/Text/ENUS.json index 03b909f..eaea153 100644 --- a/romfs/Text/ENUS.json +++ b/romfs/Text/ENUS.json @@ -21,7 +21,9 @@ "10: Error uploading file!", "11: Error processing save data meta!", "12: Error creating target directory!", - "13: Backup must be a zip to upload!" + "13: Backup must be a zip to upload!", + "14: Error mounting save data!", + "15: Error closing save data!" ], "BackupMenuStatus": [ "0: Processing save data meta file..." diff --git a/source/appstates/BackupMenuState.cpp b/source/appstates/BackupMenuState.cpp index 0cdcc12..e0ef538 100644 --- a/source/appstates/BackupMenuState.cpp +++ b/source/appstates/BackupMenuState.cpp @@ -192,6 +192,11 @@ void BackupMenuState::initialize_info_string() void BackupMenuState::save_data_check() { + const uint64_t applicationID = m_titleInfo->get_application_id(); + const FsSaveDataInfo *saveInfo = m_user->get_save_info_by_id(applicationID); + if (error::is_null(saveInfo)) { return; } + + fs::ScopedSaveMount saveMount{fs::DEFAULT_SAVE_MOUNT, saveInfo}; fslib::Directory saveRoot{fs::DEFAULT_SAVE_ROOT}; m_saveHasData = saveRoot.is_open() && saveRoot.get_count() > 0; } diff --git a/source/appstates/TextTitleSelectState.cpp b/source/appstates/TextTitleSelectState.cpp index 2684a47..4640016 100644 --- a/source/appstates/TextTitleSelectState.cpp +++ b/source/appstates/TextTitleSelectState.cpp @@ -40,7 +40,7 @@ void TextTitleSelectState::update() const bool xPressed = input::button_pressed(HidNpadButton_X); const bool yPressed = input::button_pressed(HidNpadButton_Y); - m_titleSelectMenu.update(BaseState::has_focus()); + m_titleSelectMenu.update(hasFocus); if (aPressed) { TextTitleSelectState::create_backup_menu(); } else if (xPressed) { TextTitleSelectState::create_title_option_menu(); } @@ -58,24 +58,30 @@ void TextTitleSelectState::render() void TextTitleSelectState::refresh() { + static constexpr const char *STRING_HEART = "^\uE017^ "; + m_titleSelectMenu.reset(); - for (size_t i = 0; i < m_user->get_total_data_entries(); i++) + + const size_t totalEntries = m_user->get_total_data_entries(); + for (size_t i = 0; i < totalEntries; i++) { - std::string option; - uint64_t applicationID = m_user->get_application_id_at(i); - const char *title = data::get_title_info_by_id(applicationID)->get_title(); - if (config::is_favorite(applicationID)) { option = std::string("^\uE017^ ") + title; } + const uint64_t applicationID = m_user->get_application_id_at(i); + const bool favorite = config::is_favorite(applicationID); + data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); + const char *title = titleInfo->get_title(); + + std::string option{}; + if (favorite) { option = std::string{STRING_HEART} + title; } else { option = title; } - m_titleSelectMenu.add_option(option.c_str()); + m_titleSelectMenu.add_option(option); } } void TextTitleSelectState::create_backup_menu() { - const int selected = m_titleSelectMenu.get_selected(); - const uint64_t applicationID = m_user->get_application_id_at(selected); - const FsSaveDataInfo *saveInfo = m_user->get_save_info_by_id(applicationID); - data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); + const int selected = m_titleSelectMenu.get_selected(); + const uint64_t applicationID = m_user->get_application_id_at(selected); + data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); auto backupMenuState = std::make_shared(m_user, titleInfo); StateManager::push_state(backupMenuState); diff --git a/source/appstates/TitleSelectState.cpp b/source/appstates/TitleSelectState.cpp index 56b0ac1..eaa93e0 100644 --- a/source/appstates/TitleSelectState.cpp +++ b/source/appstates/TitleSelectState.cpp @@ -74,13 +74,9 @@ bool TitleSelectState::title_count_check() void TitleSelectState::create_backup_menu() { - const int selected = m_titleView.get_selected(); - const uint64_t applicationID = m_user->get_application_id_at(selected); - const FsSaveDataInfo *saveInfo = m_user->get_save_info_at(selected); - data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); - - const bool saveMounted = fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *saveInfo); - if (!saveMounted) { return; } + const int selected = m_titleView.get_selected(); + const uint64_t applicationID = m_user->get_application_id_at(selected); + data::TitleInfo *titleInfo = data::get_title_info_by_id(applicationID); auto backupMenu = std::make_shared(m_user, titleInfo); StateManager::push_state(backupMenu); diff --git a/source/fs/SaveMetaData.cpp b/source/fs/SaveMetaData.cpp index 3aa71b8..0b73960 100644 --- a/source/fs/SaveMetaData.cpp +++ b/source/fs/SaveMetaData.cpp @@ -41,9 +41,6 @@ bool fs::fill_save_meta_data(const FsSaveDataInfo *saveInfo, fs::SaveMetaData &m bool fs::process_save_meta_data(const FsSaveDataInfo *saveInfo, const SaveMetaData &meta) { - const bool closeError = error::fslib(fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT)); - if (closeError) { return false; } - const FsSaveDataSpaceId spaceID = static_cast(saveInfo->save_data_space_id); const uint64_t saveID = saveInfo->save_data_id; @@ -57,8 +54,5 @@ bool fs::process_save_meta_data(const FsSaveDataInfo *saveInfo, const SaveMetaDa const bool extended = needsExtend && fs::extend_save_data(saveInfo, meta.saveDataSize, meta.journalSize); if (needsExtend && !extended) { return false; } - const bool reopenError = error::fslib(fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *saveInfo)); - if (reopenError) { return false; } - return true; } diff --git a/source/fs/ScopedSaveMount.cpp b/source/fs/ScopedSaveMount.cpp new file mode 100644 index 0000000..d3f0c53 --- /dev/null +++ b/source/fs/ScopedSaveMount.cpp @@ -0,0 +1,25 @@ +#include "fs/ScopedSaveMount.hpp" + +#include "error.hpp" +#include "fslib.hpp" + +fs::ScopedSaveMount::ScopedSaveMount(std::string_view mount, const FsSaveDataInfo *saveInfo) + : m_mountPoint(mount) +{ + const bool mountError = error::fslib(fslib::open_save_data_with_save_info(m_mountPoint, *saveInfo)); + m_isOpen = !mountError; +} + +fs::ScopedSaveMount::ScopedSaveMount(ScopedSaveMount &&scopedSaveMount) { *this = std::move(scopedSaveMount); } + +fs::ScopedSaveMount &fs::ScopedSaveMount::operator=(ScopedSaveMount &&scopedSaveMount) +{ + m_mountPoint = std::move(scopedSaveMount.m_mountPoint); + m_isOpen = scopedSaveMount.m_isOpen; + scopedSaveMount.m_isOpen = false; + return *this; +} + +fs::ScopedSaveMount::~ScopedSaveMount() { error::fslib(fslib::close_file_system(m_mountPoint)); } + +bool fs::ScopedSaveMount::is_open() const { return m_isOpen; } diff --git a/source/tasks/backup.cpp b/source/tasks/backup.cpp index c50b4ad..150713e 100644 --- a/source/tasks/backup.cpp +++ b/source/tasks/backup.cpp @@ -21,6 +21,7 @@ namespace // Definitions at bottom. static bool read_and_process_meta(const fslib::Path &targetDir, BackupMenuState::TaskData taskData, sys::ProgressTask *task); static bool read_and_process_meta(fs::MiniUnzip &unzip, BackupMenuState::TaskData taskData, sys::ProgressTask *task); +static fs::ScopedSaveMount create_scoped_mount(const FsSaveDataInfo *saveInfo); void tasks::backup::create_new_backup_local(sys::ProgressTask *task, data::User *user, @@ -34,6 +35,7 @@ void tasks::backup::create_new_backup_local(sys::ProgressTask *task, const bool hasZipExt = std::strstr(target.full_path(), STRING_ZIP_EXT); const uint64_t applicationID = titleInfo->get_application_id(); const FsSaveDataInfo *saveInfo = user->get_save_info_by_id(applicationID); + if (error::is_null(saveInfo)) { task->finished(); @@ -57,6 +59,7 @@ void tasks::backup::create_new_backup_local(sys::ProgressTask *task, zip.write(&saveMeta, SIZE_SAVE_META); zip.close_current_file(); } + auto scopedMount = create_scoped_mount(saveInfo); fs::copy_directory_to_zip(fs::DEFAULT_SAVE_ROOT, zip, task); } else @@ -67,8 +70,10 @@ void tasks::backup::create_new_backup_local(sys::ProgressTask *task, fslib::File saveMetaFile{saveMetaPath, FsOpenMode_Create | FsOpenMode_Write, SIZE_SAVE_META}; if (saveMetaFile.is_open()) { saveMetaFile.write(&saveMeta, SIZE_SAVE_META); } } + auto scopedMount = create_scoped_mount(saveInfo); fs::copy_directory(fs::DEFAULT_SAVE_ROOT, target, task); } + spawningState->refresh(); if (killTask) { task->finished(); } } @@ -90,21 +95,20 @@ void tasks::backup::create_new_backup_remote(sys::ProgressTask *task, const uint64_t applicationID = titleInfo->get_application_id(); const FsSaveDataInfo *saveInfo = user->get_save_info_by_id(applicationID); - const int popTicks = ui::PopMessageManager::DEFAULT_TICKS; - const char *uploadTemplate = strings::get_by_name(strings::names::IO_STATUSES, 5); - const char *popErrorCreating = strings::get_by_name(strings::names::BACKUPMENU_POPS, 5); - const char *popErrorDeleting = strings::get_by_name(strings::names::BACKUPMENU_POPS, 4); - const char *popMetaFailed = strings::get_by_name(strings::names::BACKUPMENU_POPS, 8); - const char *popErrorUploading = strings::get_by_name(strings::names::BACKUPMENU_POPS, 10); - if (error::is_null(saveInfo)) { task->finished(); return; } - // Since we're uploading this right away, no point in it being in the "right" place. const fslib::Path tempPath{STRING_JKSV_TEMP}; + const int popTicks = ui::PopMessageManager::DEFAULT_TICKS; + const char *uploadTemplate = strings::get_by_name(strings::names::IO_STATUSES, 5); + const char *popErrorDeleting = strings::get_by_name(strings::names::BACKUPMENU_POPS, 4); + const char *popErrorCreating = strings::get_by_name(strings::names::BACKUPMENU_POPS, 5); + const char *popMetaFailed = strings::get_by_name(strings::names::BACKUPMENU_POPS, 8); + const char *popErrorUploading = strings::get_by_name(strings::names::BACKUPMENU_POPS, 10); + fs::MiniZip zip{tempPath}; if (!zip.is_open()) { @@ -123,7 +127,10 @@ void tasks::backup::create_new_backup_remote(sys::ProgressTask *task, ui::PopMessageManager::push_message(popTicks, popMetaFailed); } - fs::copy_directory_to_zip(fs::DEFAULT_SAVE_ROOT, zip, task); + { + auto scopedMount = create_scoped_mount(saveInfo); + fs::copy_directory_to_zip(fs::DEFAULT_SAVE_ROOT, zip, task); + } zip.close(); { @@ -177,12 +184,12 @@ void tasks::backup::overwrite_backup_remote(sys::ProgressTask *task, BackupMenuS return; } + const fslib::Path tempPath{STRING_JKSV_TEMP}; const int popTicks = ui::PopMessageManager::DEFAULT_TICKS; const char *popErrorDeleting = strings::get_by_name(strings::names::BACKUPMENU_POPS, 4); const char *popErrorMeta = strings::get_by_name(strings::names::BACKUPMENU_POPS, 8); const char *statusUploading = strings::get_by_name(strings::names::IO_STATUSES, 5); - const fslib::Path tempPath{STRING_JKSV_TEMP}; fs::MiniZip zip{tempPath}; if (!zip.is_open()) { @@ -201,7 +208,10 @@ void tasks::backup::overwrite_backup_remote(sys::ProgressTask *task, BackupMenuS ui::PopMessageManager::push_message(popTicks, popErrorMeta); } - fs::copy_directory_to_zip(fs::DEFAULT_SAVE_ROOT, zip, task); + { + auto scopedMount = create_scoped_mount(saveInfo); + fs::copy_directory_to_zip(fs::DEFAULT_SAVE_ROOT, zip, task); + } zip.close(); { @@ -220,7 +230,6 @@ void tasks::backup::overwrite_backup_remote(sys::ProgressTask *task, BackupMenuS void tasks::backup::restore_backup_local(sys::ProgressTask *task, BackupMenuState::TaskData taskData) { static constexpr size_t SIZE_META = sizeof(fs::SaveMetaData); - if (error::is_null(task)) { return; } remote::Storage *remote = remote::get_remote_storage(); @@ -228,11 +237,21 @@ void tasks::backup::restore_backup_local(sys::ProgressTask *task, BackupMenuStat data::TitleInfo *titleInfo = taskData->titleInfo; const fslib::Path &target = taskData->path; BackupMenuState *spawningState = taskData->spawningState; + if (error::is_null(remote) || error::is_null(user) || error::is_null(titleInfo) || error::is_null(spawningState)) + { + task->finished(); + return; + } const uint64_t applicationID = titleInfo->get_application_id(); const FsSaveDataInfo *saveInfo = user->get_save_info_by_id(applicationID); const uint8_t saveType = user->get_account_save_type(); const uint64_t journalSize = titleInfo->get_journal_size(saveType); + if (error::is_null(saveInfo)) + { + task->finished(); + return; + } const bool autoBackup = config::get_by_key(config::keys::AUTO_BACKUP_ON_RESTORE); const bool autoUpload = config::get_by_key(config::keys::AUTO_UPLOAD); @@ -261,7 +280,10 @@ void tasks::backup::restore_backup_local(sys::ProgressTask *task, BackupMenuStat autoTarget = target.sub_path(lastSlash) / "AUTO - " + safeNickname + " - " + stringutil::get_date_string(); if (exportZip) { autoTarget += ".zip"; }; - tasks::backup::create_new_backup_local(task, user, titleInfo, autoTarget, spawningState, false); + { + auto scopedMount = create_scoped_mount(saveInfo); + tasks::backup::create_new_backup_local(task, user, titleInfo, autoTarget, spawningState, false); + } // Not sure if this is really needed here, but I'm sure someone will point it out. if (autoUpload && remote) @@ -293,14 +315,21 @@ void tasks::backup::restore_backup_local(sys::ProgressTask *task, BackupMenuStat } read_and_process_meta(unzip, taskData, task); + auto scopedMount = create_scoped_mount(saveInfo); fs::copy_zip_to_directory(unzip, fs::DEFAULT_SAVE_ROOT, journalSize, fs::DEFAULT_SAVE_MOUNT, task); } else if (isDir) { read_and_process_meta(target, taskData, task); + auto scopedMount = create_scoped_mount(saveInfo); fs::copy_directory_commit(target, fs::DEFAULT_SAVE_ROOT, fs::DEFAULT_SAVE_MOUNT, journalSize, task); } - else { fs::copy_file_commit(target, fs::DEFAULT_SAVE_ROOT, fs::DEFAULT_SAVE_MOUNT, journalSize, task); } + else + { + auto scopedMount = create_scoped_mount(saveInfo); + fs::copy_file_commit(target, fs::DEFAULT_SAVE_ROOT, fs::DEFAULT_SAVE_MOUNT, journalSize, task); + } + spawningState->refresh(); task->finished(); } @@ -308,10 +337,16 @@ void tasks::backup::restore_backup_local(sys::ProgressTask *task, BackupMenuStat void tasks::backup::restore_backup_remote(sys::ProgressTask *task, BackupMenuState::TaskData taskData) { if (error::is_null(task)) { return; } + data::User *user = taskData->user; data::TitleInfo *titleInfo = taskData->titleInfo; const uint64_t applicationID = titleInfo->get_application_id(); const FsSaveDataInfo *saveInfo = user->get_save_info_by_id(applicationID); + if (error::is_null(user) || error::is_null(titleInfo) || error::is_null(saveInfo)) + { + task->finished(); + return; + } const int popTicks = ui::PopMessageManager::DEFAULT_TICKS; const char *popErrorOpeningZip = strings::get_by_name(strings::names::BACKUPMENU_POPS, 3); @@ -356,7 +391,10 @@ void tasks::backup::restore_backup_remote(sys::ProgressTask *task, BackupMenuSta const uint8_t saveType = user->get_account_save_type(); const uint64_t journalSize = titleInfo->get_journal_size(saveType); read_and_process_meta(backup, taskData, task); - fs::copy_zip_to_directory(backup, fs::DEFAULT_SAVE_ROOT, journalSize, fs::DEFAULT_SAVE_MOUNT, task); + { + fs::ScopedSaveMount saveMount{fs::DEFAULT_SAVE_MOUNT, saveInfo}; + fs::copy_zip_to_directory(backup, fs::DEFAULT_SAVE_ROOT, journalSize, fs::DEFAULT_SAVE_MOUNT, task); + } backup.close(); const bool deleteError = error::fslib(fslib::delete_file(tempPath)); @@ -494,3 +532,13 @@ static bool read_and_process_meta(fs::MiniUnzip &unzip, BackupMenuState::TaskDat } return true; } + +static fs::ScopedSaveMount create_scoped_mount(const FsSaveDataInfo *saveInfo) +{ + const int popTicks = ui::PopMessageManager::DEFAULT_TICKS; + const char *popErrorMounting = strings::get_by_name(strings::names::BACKUPMENU_POPS, 14); + fs::ScopedSaveMount saveMount{fs::DEFAULT_SAVE_MOUNT, saveInfo}; + if (!saveMount.is_open()) { ui::PopMessageManager::push_message(popTicks, popErrorMounting); } + + return saveMount; +}