delete save file before restoring. always commit fs after every write + close, delete, rename, create.
This commit is contained in:
@@ -199,6 +199,7 @@ struct File {
|
|||||||
FsFile m_native{};
|
FsFile m_native{};
|
||||||
std::FILE* m_stdio{};
|
std::FILE* m_stdio{};
|
||||||
s64 m_stdio_off{};
|
s64 m_stdio_off{};
|
||||||
|
u32 m_mode{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dir {
|
struct Dir {
|
||||||
@@ -287,6 +288,7 @@ struct Fs {
|
|||||||
virtual Result GetEntryType(const FsPath& path, FsDirEntryType* out) = 0;
|
virtual Result GetEntryType(const FsPath& path, FsDirEntryType* out) = 0;
|
||||||
virtual Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) = 0;
|
virtual Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) = 0;
|
||||||
virtual Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts) = 0;
|
virtual Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts) = 0;
|
||||||
|
virtual Result Commit() = 0;
|
||||||
virtual bool FileExists(const FsPath& path) = 0;
|
virtual bool FileExists(const FsPath& path) = 0;
|
||||||
virtual bool DirExists(const FsPath& path) = 0;
|
virtual bool DirExists(const FsPath& path) = 0;
|
||||||
virtual bool IsNative() const = 0;
|
virtual bool IsNative() const = 0;
|
||||||
@@ -362,6 +364,9 @@ struct FsStdio : Fs {
|
|||||||
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
|
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
|
||||||
return fs::SetTimestamp(path, ts);
|
return fs::SetTimestamp(path, ts);
|
||||||
}
|
}
|
||||||
|
Result Commit() override {
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
bool FileExists(const FsPath& path) override {
|
bool FileExists(const FsPath& path) override {
|
||||||
return fs::FileExists(path);
|
return fs::FileExists(path);
|
||||||
}
|
}
|
||||||
@@ -397,10 +402,6 @@ struct FsNative : Fs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Commit() {
|
|
||||||
return fsFsCommit(&m_fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetFreeSpace(const FsPath& path, s64* out) {
|
Result GetFreeSpace(const FsPath& path, s64* out) {
|
||||||
return fsFsGetFreeSpace(&m_fs, path, out);
|
return fsFsGetFreeSpace(&m_fs, path, out);
|
||||||
}
|
}
|
||||||
@@ -453,6 +454,9 @@ struct FsNative : Fs {
|
|||||||
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
|
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
|
||||||
return fs::SetTimestamp(&m_fs, path, ts);
|
return fs::SetTimestamp(&m_fs, path, ts);
|
||||||
}
|
}
|
||||||
|
Result Commit() override {
|
||||||
|
return fsFsCommit(&m_fs);
|
||||||
|
}
|
||||||
bool FileExists(const FsPath& path) override {
|
bool FileExists(const FsPath& path) override {
|
||||||
return fs::FileExists(&m_fs, path);
|
return fs::FileExists(&m_fs, path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "ui/menus/menu_base.hpp"
|
#include "ui/menus/menu_base.hpp"
|
||||||
#include "ui/scrolling_text.hpp"
|
#include "ui/scrolling_text.hpp"
|
||||||
|
#include "ui/progress_box.hpp"
|
||||||
#include "ui/list.hpp"
|
#include "ui/list.hpp"
|
||||||
#include "fs.hpp"
|
#include "fs.hpp"
|
||||||
#include "option.hpp"
|
#include "option.hpp"
|
||||||
@@ -183,6 +184,7 @@ struct FsView final : Widget {
|
|||||||
|
|
||||||
void SetSide(ViewSide side);
|
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_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;
|
static auto get_collections(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result;
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
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);
|
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) {
|
Result CreateDirectory(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) {
|
||||||
R_UNLESS(ignore_read_only || !is_read_only_root(path), Result_FsReadOnly);
|
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) {
|
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{};
|
FsPath new_path{};
|
||||||
std::snprintf(new_path, sizeof(new_path), "%.*s", (int)(last_slash - _path.s), _path.s);
|
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) {
|
Result DeleteFile(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) {
|
||||||
R_UNLESS(ignore_read_only || !is_read_only(path), Result_FsReadOnly);
|
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) {
|
Result DeleteDirectory(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) {
|
||||||
R_UNLESS(ignore_read_only || !is_read_only(path), Result_FsReadOnly);
|
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) {
|
Result DeleteDirectoryRecursively(FsFileSystem* fs, const FsPath& path, bool ignore_read_only) {
|
||||||
R_UNLESS(ignore_read_only || !is_read_only(path), Result_FsReadOnly);
|
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) {
|
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(src), Result_FsReadOnly);
|
||||||
R_UNLESS(ignore_read_only || !is_read_only(dst), 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) {
|
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(src), Result_FsReadOnly);
|
||||||
R_UNLESS(ignore_read_only || !is_read_only(dst), 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) {
|
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};
|
FsNative fs{_fs, false, ignore_read_only};
|
||||||
R_TRY(fs.GetFsOpenResult());
|
R_TRY(fs.GetFsOpenResult());
|
||||||
|
ON_SCOPE_EXIT(fs.Commit());
|
||||||
|
|
||||||
if (auto rc = fs.CreateFile(path, in.size(), 0); R_FAILED(rc) && rc != FsError_PathAlreadyExists) {
|
if (auto rc = fs.CreateFile(path, in.size(), 0); R_FAILED(rc) && rc != FsError_PathAlreadyExists) {
|
||||||
return rc;
|
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) {
|
Result OpenFile(fs::Fs* fs, const fs::FsPath& path, u32 mode, File* f) {
|
||||||
f->m_fs = fs;
|
f->m_fs = fs;
|
||||||
|
f->m_mode = mode;
|
||||||
|
|
||||||
if (f->m_fs->IsNative()) {
|
if (f->m_fs->IsNative()) {
|
||||||
auto fs = (fs::FsNative*)f->m_fs;
|
auto fs = (fs::FsNative*)f->m_fs;
|
||||||
@@ -594,6 +612,9 @@ void File::Close() {
|
|||||||
if (m_fs->IsNative()) {
|
if (m_fs->IsNative()) {
|
||||||
if (serviceIsActive(&m_native.s)) {
|
if (serviceIsActive(&m_native.s)) {
|
||||||
fsFileClose(&m_native);
|
fsFileClose(&m_native);
|
||||||
|
if (m_mode & FsOpenMode_Write) {
|
||||||
|
m_fs->Commit();
|
||||||
|
}
|
||||||
m_native = {};
|
m_native = {};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1185,7 +1185,7 @@ void FsView::OnDeleteCallback() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeleteAllCollections(pbox, src_fs, selected, collections);
|
return DeleteAllCollectionsWithSelected(pbox, src_fs, selected, collections);
|
||||||
}, [this](Result rc){
|
}, [this](Result rc){
|
||||||
App::PushErrorBox(rc, "Failed to, TODO: add message here"_i18n);
|
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
|
// 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).
|
// reverse order so that the folder can be deleted (it must be empty).
|
||||||
if (selected.m_type == SelectedType::Cut) {
|
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);
|
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
|
// delete everything in collections, reversed
|
||||||
for (const auto& c : std::views::reverse(collections)) {
|
for (const auto& c : std::views::reverse(collections)) {
|
||||||
const auto delete_func = [&](auto& array) -> Result {
|
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_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) {
|
for (const auto& p : selected.m_files) {
|
||||||
pbox->Yield();
|
pbox->Yield();
|
||||||
R_TRY(pbox->ShouldExitResult());
|
R_TRY(pbox->ShouldExitResult());
|
||||||
|
|||||||
@@ -1197,8 +1197,7 @@ Result Menu::RestoreSaveInternal(ProgressBox* pbox, const Entry& e, const fs::Fs
|
|||||||
ON_SCOPE_EXIT(unzClose(zfile));
|
ON_SCOPE_EXIT(unzClose(zfile));
|
||||||
log_write("opened zip\n");
|
log_write("opened zip\n");
|
||||||
|
|
||||||
bool has_meta{};
|
std::optional<NXSaveMeta> meta{};
|
||||||
NXSaveMeta meta{};
|
|
||||||
|
|
||||||
// get manifest
|
// get manifest
|
||||||
if (UNZ_END_OF_LIST_OF_FILE != unzLocateFile(zfile, NX_SAVE_META_NAME, 0)) {
|
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");
|
log_write("opened meta file\n");
|
||||||
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
|
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
|
||||||
|
|
||||||
const auto len = unzReadCurrentFile(zfile, &meta, sizeof(meta));
|
NXSaveMeta temp_meta;
|
||||||
if (len == sizeof(meta) && meta.magic == NX_SAVE_META_MAGIC && meta.version == NX_SAVE_META_VERSION) {
|
const auto len = unzReadCurrentFile(zfile, &temp_meta, sizeof(temp_meta));
|
||||||
has_meta = true;
|
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");
|
log_write("loaded meta!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_meta) {
|
if (meta.has_value()) {
|
||||||
log_write("extending save file\n");
|
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");
|
log_write("extended save file\n");
|
||||||
} else {
|
} else {
|
||||||
log_write("doing manual meta parse\n");
|
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};
|
fs::FsNativeSave save_fs{(FsSaveDataType)e.save_data_type, save_data_space_id, &attr, false};
|
||||||
R_TRY(save_fs.GetFsOpenResult());
|
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");
|
log_write("opened save file\n");
|
||||||
// restore save data from zip.
|
// restore save data from zip.
|
||||||
R_TRY(thread::TransferUnzipAll(pbox, zfile, &save_fs, "/", [&](const fs::FsPath& name, fs::FsPath& path) -> bool {
|
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.
|
// restore everything else.
|
||||||
log_write("restoring: %s\n", path.s);
|
log_write("restoring: %s\n", path.s);
|
||||||
|
|
||||||
// commit after every save otherwise FsError_MappingTableFull is thrown.
|
|
||||||
R_TRY(save_fs.Commit());
|
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
log_write("finished, doing commit\n");
|
log_write("finished save backup\n");
|
||||||
R_TRY(save_fs.Commit());
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user