diff --git a/sphaira/include/fs.hpp b/sphaira/include/fs.hpp index 356e4ed..46e93c0 100644 --- a/sphaira/include/fs.hpp +++ b/sphaira/include/fs.hpp @@ -199,6 +199,7 @@ struct File { FsFile m_native{}; std::FILE* m_stdio{}; s64 m_stdio_off{}; + u32 m_mode{}; }; struct Dir { @@ -287,6 +288,7 @@ struct Fs { virtual Result GetEntryType(const FsPath& path, FsDirEntryType* out) = 0; virtual Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) = 0; virtual Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts) = 0; + virtual Result Commit() = 0; virtual bool FileExists(const FsPath& path) = 0; virtual bool DirExists(const FsPath& path) = 0; virtual bool IsNative() const = 0; @@ -362,6 +364,9 @@ struct FsStdio : Fs { Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override { return fs::SetTimestamp(path, ts); } + Result Commit() override { + R_SUCCEED(); + } bool FileExists(const FsPath& path) override { return fs::FileExists(path); } @@ -397,10 +402,6 @@ struct FsNative : Fs { } } - Result Commit() { - return fsFsCommit(&m_fs); - } - Result GetFreeSpace(const FsPath& path, s64* out) { return fsFsGetFreeSpace(&m_fs, path, out); } @@ -453,6 +454,9 @@ struct FsNative : Fs { Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override { return fs::SetTimestamp(&m_fs, path, ts); } + Result Commit() override { + return fsFsCommit(&m_fs); + } bool FileExists(const FsPath& path) override { return fs::FileExists(&m_fs, path); } diff --git a/sphaira/include/ui/menus/filebrowser.hpp b/sphaira/include/ui/menus/filebrowser.hpp index 8d2c66f..58ba1e0 100644 --- a/sphaira/include/ui/menus/filebrowser.hpp +++ b/sphaira/include/ui/menus/filebrowser.hpp @@ -2,6 +2,7 @@ #include "ui/menus/menu_base.hpp" #include "ui/scrolling_text.hpp" +#include "ui/progress_box.hpp" #include "ui/list.hpp" #include "fs.hpp" #include "option.hpp" @@ -183,6 +184,7 @@ struct FsView final : Widget { void SetSide(ViewSide side); + static Result DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const FsDirCollections& collections, u32 mode = FsDirOpenMode_ReadDirs|FsDirOpenMode_ReadFiles); static auto get_collection(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result; static auto get_collections(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result; diff --git a/sphaira/source/fs.cpp b/sphaira/source/fs.cpp index fb4625c..13e4998 100644 --- a/sphaira/source/fs.cpp +++ b/sphaira/source/fs.cpp @@ -112,13 +112,17 @@ FsPath AppendPath(const FsPath& root_path, const FsPath& _file_path) { Result CreateFile(FsFileSystem* fs, const FsPath& path, u64 size, u32 option, bool ignore_read_only) { R_UNLESS(ignore_read_only || !is_read_only_root(path), Result_FsReadOnly); - return fsFsCreateFile(fs, path, size, option); + R_TRY(fsFsCreateFile(fs, path, size, option)); + fsFsCommit(fs); + R_SUCCEED(); } Result CreateDirectory(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) { R_UNLESS(ignore_read_only || !is_read_only_root(path), Result_FsReadOnly); - return fsFsCreateDirectory(fs, path); + R_TRY(fsFsCreateDirectory(fs, path)); + fsFsCommit(fs); + R_SUCCEED(); } Result CreateDirectoryRecursively(FsFileSystem* fs, const FsPath& _path, bool ignore_read_only) { @@ -180,38 +184,50 @@ Result CreateDirectoryRecursivelyWithPath(FsFileSystem* fs, const FsPath& _path, FsPath new_path{}; std::snprintf(new_path, sizeof(new_path), "%.*s", (int)(last_slash - _path.s), _path.s); - return CreateDirectoryRecursively(fs, new_path, ignore_read_only); + R_TRY(CreateDirectoryRecursively(fs, new_path, ignore_read_only)); + fsFsCommit(fs); + R_SUCCEED(); } Result DeleteFile(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) { R_UNLESS(ignore_read_only || !is_read_only(path), Result_FsReadOnly); - return fsFsDeleteFile(fs, path); + R_TRY(fsFsDeleteFile(fs, path)); + fsFsCommit(fs); + R_SUCCEED(); } Result DeleteDirectory(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) { R_UNLESS(ignore_read_only || !is_read_only(path), Result_FsReadOnly); - return fsFsDeleteDirectory(fs, path); + R_TRY(fsFsDeleteDirectory(fs, path)); + fsFsCommit(fs); + R_SUCCEED(); } Result DeleteDirectoryRecursively(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) { R_UNLESS(ignore_read_only || !is_read_only(path), Result_FsReadOnly); - return fsFsDeleteDirectoryRecursively(fs, path); + R_TRY(fsFsDeleteDirectoryRecursively(fs, path)); + fsFsCommit(fs); + R_SUCCEED(); } Result RenameFile(FsFileSystem* fs, const FsPath& src, const FsPath& dst, bool ignore_read_only) { R_UNLESS(ignore_read_only || !is_read_only(src), Result_FsReadOnly); R_UNLESS(ignore_read_only || !is_read_only(dst), Result_FsReadOnly); - return fsFsRenameFile(fs, src, dst); + R_TRY(fsFsRenameFile(fs, src, dst)); + fsFsCommit(fs); + R_SUCCEED(); } Result RenameDirectory(FsFileSystem* fs, const FsPath& src, const FsPath& dst, bool ignore_read_only) { R_UNLESS(ignore_read_only || !is_read_only(src), Result_FsReadOnly); R_UNLESS(ignore_read_only || !is_read_only(dst), Result_FsReadOnly); - return fsFsRenameDirectory(fs, src, dst); + R_TRY(fsFsRenameDirectory(fs, src, dst)); + fsFsCommit(fs); + R_SUCCEED(); } Result GetEntryType(FsFileSystem* fs, const FsPath& path, FsDirEntryType* out) { @@ -263,6 +279,7 @@ Result write_entire_file(FsFileSystem* _fs, const FsPath& path, const std::vecto FsNative fs{_fs, false, ignore_read_only}; R_TRY(fs.GetFsOpenResult()); + ON_SCOPE_EXIT(fs.Commit()); if (auto rc = fs.CreateFile(path, in.size(), 0); R_FAILED(rc) && rc != FsError_PathAlreadyExists) { return rc; @@ -484,6 +501,7 @@ Result copy_entire_file(const FsPath& dst, const FsPath& src, bool ignore_read_o Result OpenFile(fs::Fs* fs, const fs::FsPath& path, u32 mode, File* f) { f->m_fs = fs; + f->m_mode = mode; if (f->m_fs->IsNative()) { auto fs = (fs::FsNative*)f->m_fs; @@ -594,6 +612,9 @@ void File::Close() { if (m_fs->IsNative()) { if (serviceIsActive(&m_native.s)) { fsFileClose(&m_native); + if (m_mode & FsOpenMode_Write) { + m_fs->Commit(); + } m_native = {}; } } else { diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index eedefd1..1d66ac0 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -1185,7 +1185,7 @@ void FsView::OnDeleteCallback() { } } - return DeleteAllCollections(pbox, src_fs, selected, collections); + return DeleteAllCollectionsWithSelected(pbox, src_fs, selected, collections); }, [this](Result rc){ App::PushErrorBox(rc, "Failed to, TODO: add message here"_i18n); @@ -1319,7 +1319,7 @@ void FsView::OnPasteCallback() { // the folders cannot be deleted until the end as they have to be removed in // reverse order so that the folder can be deleted (it must be empty). if (selected.m_type == SelectedType::Cut) { - R_TRY(DeleteAllCollections(pbox, src_fs, selected, collections, FsDirOpenMode_ReadDirs)); + R_TRY(DeleteAllCollectionsWithSelected(pbox, src_fs, selected, collections, FsDirOpenMode_ReadDirs)); } } @@ -1430,7 +1430,7 @@ auto FsView::get_collections(const fs::FsPath& path, const fs::FsPath& parent_na return get_collections(m_fs.get(), path, parent_name, out, inc_size); } -static Result DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const SelectedStash& selected, const FsDirCollections& collections, u32 mode = FsDirOpenMode_ReadDirs|FsDirOpenMode_ReadFiles) { +Result FsView::DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const FsDirCollections& collections, u32 mode) { // delete everything in collections, reversed for (const auto& c : std::views::reverse(collections)) { const auto delete_func = [&](auto& array) -> Result { @@ -1459,6 +1459,12 @@ static Result DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const Selected R_TRY(delete_func(c.dirs)); } + R_SUCCEED(); +} + +static Result DeleteAllCollectionsWithSelected(ProgressBox* pbox, fs::Fs* fs, const SelectedStash& selected, const FsDirCollections& collections, u32 mode = FsDirOpenMode_ReadDirs|FsDirOpenMode_ReadFiles) { + R_TRY(FsView::DeleteAllCollections(pbox, fs, collections, mode)); + for (const auto& p : selected.m_files) { pbox->Yield(); R_TRY(pbox->ShouldExitResult()); diff --git a/sphaira/source/ui/menus/save_menu.cpp b/sphaira/source/ui/menus/save_menu.cpp index cc9bd66..d333145 100644 --- a/sphaira/source/ui/menus/save_menu.cpp +++ b/sphaira/source/ui/menus/save_menu.cpp @@ -1197,8 +1197,7 @@ Result Menu::RestoreSaveInternal(ProgressBox* pbox, const Entry& e, const fs::Fs ON_SCOPE_EXIT(unzClose(zfile)); log_write("opened zip\n"); - bool has_meta{}; - NXSaveMeta meta{}; + std::optional meta{}; // get manifest if (UNZ_END_OF_LIST_OF_FILE != unzLocateFile(zfile, NX_SAVE_META_NAME, 0)) { @@ -1207,17 +1206,18 @@ Result Menu::RestoreSaveInternal(ProgressBox* pbox, const Entry& e, const fs::Fs log_write("opened meta file\n"); ON_SCOPE_EXIT(unzCloseCurrentFile(zfile)); - const auto len = unzReadCurrentFile(zfile, &meta, sizeof(meta)); - if (len == sizeof(meta) && meta.magic == NX_SAVE_META_MAGIC && meta.version == NX_SAVE_META_VERSION) { - has_meta = true; + NXSaveMeta temp_meta; + const auto len = unzReadCurrentFile(zfile, &temp_meta, sizeof(temp_meta)); + if (len == sizeof(temp_meta) && temp_meta.magic == NX_SAVE_META_MAGIC && temp_meta.version == NX_SAVE_META_VERSION) { + meta = temp_meta; log_write("loaded meta!\n"); } } } - if (has_meta) { + if (meta.has_value()) { log_write("extending save file\n"); - R_TRY(fsExtendSaveDataFileSystem(save_data_space_id, e.save_data_id, meta.data_size, meta.journal_size)); + R_TRY(fsExtendSaveDataFileSystem(save_data_space_id, e.save_data_id, meta->data_size, meta->journal_size)); log_write("extended save file\n"); } else { log_write("doing manual meta parse\n"); @@ -1267,6 +1267,11 @@ Result Menu::RestoreSaveInternal(ProgressBox* pbox, const Entry& e, const fs::Fs fs::FsNativeSave save_fs{(FsSaveDataType)e.save_data_type, save_data_space_id, &attr, false}; R_TRY(save_fs.GetFsOpenResult()); + // delete all files in save. + filebrowser::FsDirCollections collections; + R_TRY(filebrowser::FsView::get_collections(&save_fs, "/", "", collections)); + R_TRY(filebrowser::FsView::DeleteAllCollections(pbox, &save_fs, collections)); + log_write("opened save file\n"); // restore save data from zip. R_TRY(thread::TransferUnzipAll(pbox, zfile, &save_fs, "/", [&](const fs::FsPath& name, fs::FsPath& path) -> bool { @@ -1278,14 +1283,10 @@ Result Menu::RestoreSaveInternal(ProgressBox* pbox, const Entry& e, const fs::Fs // restore everything else. log_write("restoring: %s\n", path.s); - - // commit after every save otherwise FsError_MappingTableFull is thrown. - R_TRY(save_fs.Commit()); return true; })); - log_write("finished, doing commit\n"); - R_TRY(save_fs.Commit()); + log_write("finished save backup\n"); R_SUCCEED(); }