diff --git a/sphaira/include/utils/devoptab_common.hpp b/sphaira/include/utils/devoptab_common.hpp index c48a09a..615d77f 100644 --- a/sphaira/include/utils/devoptab_common.hpp +++ b/sphaira/include/utils/devoptab_common.hpp @@ -5,6 +5,9 @@ namespace sphaira::devoptab::common { +// max entries per devoptab, should be enough. +enum { MAX_ENTRIES = 4 }; + // buffers data in 512k chunks to maximise throughput. // not suitable if random access >= 512k is common. // if that is needed, see the LRU cache varient used for fatfs. diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index a744cdf..a1df374 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -2363,7 +2363,8 @@ void MountFsHelper(const std::shared_ptr& fs, const fs::FsPath& name) { .flags = filebrowser::FsEntryFlag_ReadOnly, }; - App::Push(fs, fs_entry, fs->Root()); + const auto options = FsOption_All &~ FsOption_LoadAssoc; + App::Push(fs, fs_entry, fs->Root(), options); } } // namespace sphaira::ui::menu::filebrowser diff --git a/sphaira/source/utils/devoptab_nsp.cpp b/sphaira/source/utils/devoptab_nsp.cpp index f5f8427..5c0ded4 100644 --- a/sphaira/source/utils/devoptab_nsp.cpp +++ b/sphaira/source/utils/devoptab_nsp.cpp @@ -9,8 +9,9 @@ #include "yati/source/file.hpp" #include -#include #include +#include +#include #include #include @@ -164,8 +165,6 @@ int devoptab_dirclose(struct _reent *r, DIR_ITER *dirState) { int devoptab_lstat(struct _reent *r, const char *_path, struct stat *st) { auto device = (Device*)r->deviceData; - log_write("[\t\tDEV] lstat\n"); - char path[FS_MAX_PATH]; if (!common::fix_path(_path, path)) { return set_errno(r, ENOENT); @@ -210,17 +209,20 @@ constexpr devoptab_t DEVOPTAB = { }; struct Entry { - Device device; - devoptab_t devoptab; - fs::FsPath path; - fs::FsPath mount; - char name[32]; - s32 ref_count; + Device device{}; + devoptab_t devoptab{}; + fs::FsPath path{}; + fs::FsPath mount{}; + char name[32]{}; + s32 ref_count{}; + + ~Entry() { + RemoveDevice(mount); + } }; Mutex g_mutex; -std::vector g_entries; -u32 g_mount_idx; +std::array, common::MAX_ENTRIES> g_entries; } // namespace @@ -229,13 +231,20 @@ Result MountNsp(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { // check if we already have the save mounted. for (auto& e : g_entries) { - if (e.path == path) { - e.ref_count++; - out_path = e.mount; + if (e && e->path == path) { + e->ref_count++; + out_path = e->mount; R_SUCCEED(); } } + // otherwise, find next free entry. + auto itr = std::ranges::find_if(g_entries, [](auto& e){ + return !e; + }); + R_UNLESS(itr != g_entries.end(), 0x1); + + const auto index = std::distance(g_entries.begin(), itr); auto source = std::make_unique(fs, path); s64 size; @@ -246,22 +255,22 @@ Result MountNsp(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { yati::container::Collections collections; R_TRY(nsp.GetCollections(collections)); - auto& entry = g_entries.emplace_back(); - entry.path = path; - entry.devoptab = DEVOPTAB; - entry.devoptab.name = entry.name; - entry.devoptab.deviceData = &entry.device; - entry.device.source = std::move(buffered); - entry.device.collections = collections; - std::snprintf(entry.name, sizeof(entry.name), "nsp_%u", g_mount_idx); - std::snprintf(entry.mount, sizeof(entry.mount), "nsp_%u:/", g_mount_idx); + auto entry = std::make_unique(); + entry->path = path; + entry->devoptab = DEVOPTAB; + entry->devoptab.name = entry->name; + entry->devoptab.deviceData = &entry->device; + entry->device.source = std::move(buffered); + entry->device.collections = collections; + std::snprintf(entry->name, sizeof(entry->name), "nsp_%zu", index); + std::snprintf(entry->mount, sizeof(entry->mount), "nsp_%zu:/", index); - R_UNLESS(AddDevice(&entry.devoptab) >= 0, 0x1); - log_write("[NSP] DEVICE SUCCESS %s %s\n", path.s, entry.name); + R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); + log_write("[NSP] DEVICE SUCCESS %s %s\n", path.s, entry->name); - out_path = entry.mount; - entry.ref_count++; - g_mount_idx++; + out_path = entry->mount; + entry->ref_count++; + *itr = std::move(entry); R_SUCCEED(); } @@ -270,20 +279,19 @@ void UmountNsp(const fs::FsPath& mount) { SCOPED_MUTEX(&g_mutex); auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return mount == e.mount; + return e && e->mount == mount; }); if (itr == g_entries.end()) { return; } - if (itr->ref_count) { - itr->ref_count--; + if ((*itr)->ref_count) { + (*itr)->ref_count--; } - if (!itr->ref_count) { - RemoveDevice(mount); - g_entries.erase(itr); + if (!(*itr)->ref_count) { + itr->reset(); } } diff --git a/sphaira/source/utils/devoptab_save.cpp b/sphaira/source/utils/devoptab_save.cpp index f6d7b77..861a3f2 100644 --- a/sphaira/source/utils/devoptab_save.cpp +++ b/sphaira/source/utils/devoptab_save.cpp @@ -8,8 +8,9 @@ #include "yati/nx/nxdumptool/core/save.h" #include -#include #include +#include +#include #include #include @@ -243,19 +244,21 @@ constexpr devoptab_t DEVOPTAB = { }; struct Entry { - u64 id; - Device device; - devoptab_t devoptab; - char name[32]; - s32 ref_count; + Device device{}; + devoptab_t devoptab{}; + u64 id{}; + fs::FsPath mount{}; + char name[32]{}; + s32 ref_count{}; + + ~Entry() { + RemoveDevice(mount); + save_close_savefile(&device.ctx); + } }; Mutex g_mutex; -std::vector g_entries; - -void MakeMountPath(u64 id, fs::FsPath& out_path) { - std::snprintf(out_path, sizeof(out_path), "%016lx:/", id); -} +std::array, common::MAX_ENTRIES> g_entries; } // namespace @@ -264,13 +267,19 @@ Result MountFromSavePath(u64 id, fs::FsPath& out_path) { // check if we already have the save mounted. for (auto& e : g_entries) { - if (e.id == id) { - e.ref_count++; - MakeMountPath(id, out_path); + if (e && e->id == id) { + e->ref_count++; + out_path = e->mount; R_SUCCEED(); } } + // otherwise, find next free entry. + auto itr = std::ranges::find_if(g_entries, [](auto& e){ + return !e; + }); + R_UNLESS(itr != g_entries.end(), 0x1); + char path[256]; std::snprintf(path, sizeof(path), "SYSTEM:/save/%016lx", id); @@ -281,21 +290,23 @@ Result MountFromSavePath(u64 id, fs::FsPath& out_path) { log_write("[SAVE] OPEN SUCCESS %s\n", path); - auto& entry = g_entries.emplace_back(); - entry.id = id; - entry.device.ctx = ctx; - entry.device.file_table = &ctx->save_filesystem_core.file_table; - entry.devoptab = DEVOPTAB; - entry.devoptab.name = entry.name; - entry.devoptab.deviceData = &entry.device; - std::snprintf(entry.name, sizeof(entry.name), "%016lx", id); + auto entry = std::make_unique(); + entry->id = id; + entry->device.ctx = ctx; + entry->device.file_table = &ctx->save_filesystem_core.file_table; + entry->devoptab = DEVOPTAB; + entry->devoptab.name = entry->name; + entry->devoptab.deviceData = &entry->device; + std::snprintf(entry->name, sizeof(entry->name), "%016lx", id); + std::snprintf(entry->mount, sizeof(entry->mount), "%016lx:/", id); - R_UNLESS(AddDevice(&entry.devoptab) >= 0, 0x1); - log_write("[SAVE] DEVICE SUCCESS %s %s\n", path, entry.name); + R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); + log_write("[SAVE] DEVICE SUCCESS %s %s\n", path, entry->name); - MakeMountPath(id, out_path); + out_path = entry->mount; + entry->ref_count++; + *itr = std::move(entry); - entry.ref_count++; R_SUCCEED(); } @@ -303,29 +314,19 @@ void UnmountSave(u64 id) { SCOPED_MUTEX(&g_mutex); auto itr = std::ranges::find_if(g_entries, [id](auto& e){ - return id == e.id; + return e && e->id == id; }); if (itr == g_entries.end()) { return; } - if (itr->ref_count) { - itr->ref_count--; + if ((*itr)->ref_count) { + (*itr)->ref_count--; } - if (!itr->ref_count) { - fs::FsPath path; - MakeMountPath(id, path); - - // todo: verify this actually works. - RemoveDevice(path); - - if (itr->device.ctx) { - save_close_savefile(&itr->device.ctx); - } - - g_entries.erase(itr); + if (!(*itr)->ref_count) { + itr->reset(); } } diff --git a/sphaira/source/utils/devoptab_xci.cpp b/sphaira/source/utils/devoptab_xci.cpp index 26e668d..45bd314 100644 --- a/sphaira/source/utils/devoptab_xci.cpp +++ b/sphaira/source/utils/devoptab_xci.cpp @@ -8,8 +8,9 @@ #include "yati/source/file.hpp" #include -#include #include +#include +#include #include #include @@ -230,17 +231,20 @@ constexpr devoptab_t DEVOPTAB = { }; struct Entry { - Device device; - devoptab_t devoptab; - fs::FsPath path; - fs::FsPath mount; - char name[32]; - s32 ref_count; + Device device{}; + devoptab_t devoptab{}; + fs::FsPath path{}; + fs::FsPath mount{}; + char name[32]{}; + s32 ref_count{}; + + ~Entry() { + RemoveDevice(mount); + } }; Mutex g_mutex; -std::vector g_entries; -u32 g_mount_idx; +std::array, common::MAX_ENTRIES> g_entries; } // namespace @@ -249,13 +253,20 @@ Result MountXci(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { // check if we already have the save mounted. for (auto& e : g_entries) { - if (e.path == path) { - e.ref_count++; - out_path = e.mount; + if (e && e->path == path) { + e->ref_count++; + out_path = e->mount; R_SUCCEED(); } } + // otherwise, find next free entry. + auto itr = std::ranges::find_if(g_entries, [](auto& e){ + return !e; + }); + R_UNLESS(itr != g_entries.end(), 0x1); + + const auto index = std::distance(g_entries.begin(), itr); auto source = std::make_unique(fs, path); s64 size; @@ -266,22 +277,22 @@ Result MountXci(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { yati::container::Xci::Partitions partitions; R_TRY(xci.GetPartitions(partitions)); - auto& entry = g_entries.emplace_back(); - entry.path = path; - entry.devoptab = DEVOPTAB; - entry.devoptab.name = entry.name; - entry.devoptab.deviceData = &entry.device; - entry.device.source = std::move(buffered); - entry.device.partitions = partitions; - std::snprintf(entry.name, sizeof(entry.name), "xci_%u", g_mount_idx); - std::snprintf(entry.mount, sizeof(entry.mount), "xci_%u:/", g_mount_idx); + auto entry = std::make_unique(); + entry->path = path; + entry->devoptab = DEVOPTAB; + entry->devoptab.name = entry->name; + entry->devoptab.deviceData = &entry->device; + entry->device.source = std::move(buffered); + entry->device.partitions = partitions; + std::snprintf(entry->name, sizeof(entry->name), "xci_%zu", index); + std::snprintf(entry->mount, sizeof(entry->mount), "xci_%zu:/", index); - R_UNLESS(AddDevice(&entry.devoptab) >= 0, 0x1); - log_write("[XCI] DEVICE SUCCESS %s %s\n", path.s, entry.name); + R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); + log_write("[XCI] DEVICE SUCCESS %s %s\n", path.s, entry->name); - out_path = entry.mount; - entry.ref_count++; - g_mount_idx++; + out_path = entry->mount; + entry->ref_count++; + *itr = std::move(entry); R_SUCCEED(); } @@ -290,20 +301,19 @@ void UmountXci(const fs::FsPath& mount) { SCOPED_MUTEX(&g_mutex); auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return mount == e.mount; + return e && e->mount == mount; }); if (itr == g_entries.end()) { return; } - if (itr->ref_count) { - itr->ref_count--; + if ((*itr)->ref_count) { + (*itr)->ref_count--; } - if (!itr->ref_count) { - RemoveDevice(mount); - g_entries.erase(itr); + if (!(*itr)->ref_count) { + itr->reset(); } } diff --git a/sphaira/source/utils/devoptab_zip.cpp b/sphaira/source/utils/devoptab_zip.cpp index fe2b662..5349ed5 100644 --- a/sphaira/source/utils/devoptab_zip.cpp +++ b/sphaira/source/utils/devoptab_zip.cpp @@ -6,8 +6,9 @@ #include "yati/source/file.hpp" #include -#include #include +#include +#include #include #include #include @@ -600,17 +601,20 @@ Result ParseZip(common::BufferedData* source, s64 size, FileTableEntries& out) { } struct Entry { - Device device; - devoptab_t devoptab; - fs::FsPath path; - fs::FsPath mount; - char name[32]; - s32 ref_count; + Device device{}; + devoptab_t devoptab{}; + fs::FsPath path{}; + fs::FsPath mount{}; + char name[32]{}; + s32 ref_count{}; + + ~Entry() { + RemoveDevice(mount); + } }; Mutex g_mutex; -std::vector g_entries; -u32 g_mount_idx; +std::array, common::MAX_ENTRIES> g_entries; } // namespace @@ -619,13 +623,20 @@ Result MountZip(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { // check if we already have the save mounted. for (auto& e : g_entries) { - if (e.path == path) { - e.ref_count++; - out_path = e.mount; + if (e && e->path == path) { + e->ref_count++; + out_path = e->mount; R_SUCCEED(); } } + // otherwise, find next free entry. + auto itr = std::ranges::find_if(g_entries, [](auto& e){ + return !e; + }); + R_UNLESS(itr != g_entries.end(), 0x1); + + const auto index = std::distance(g_entries.begin(), itr); auto source = std::make_unique(fs, path); s64 size; @@ -640,22 +651,22 @@ Result MountZip(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& out_path) { DirectoryEntry root; Parse(table_entries, root); - auto& entry = g_entries.emplace_back(); - entry.path = path; - entry.devoptab = DEVOPTAB; - entry.devoptab.name = entry.name; - entry.devoptab.deviceData = &entry.device; - entry.device.source = std::move(buffered); - entry.device.root = root; - std::snprintf(entry.name, sizeof(entry.name), "zip_%u", g_mount_idx); - std::snprintf(entry.mount, sizeof(entry.mount), "zip_%u:/", g_mount_idx); + auto entry = std::make_unique(); + entry->path = path; + entry->devoptab = DEVOPTAB; + entry->devoptab.name = entry->name; + entry->devoptab.deviceData = &entry->device; + entry->device.source = std::move(buffered); + entry->device.root = root; + std::snprintf(entry->name, sizeof(entry->name), "zip_%zu", index); + std::snprintf(entry->mount, sizeof(entry->mount), "zip_%zu:/", index); - R_UNLESS(AddDevice(&entry.devoptab) >= 0, 0x1); - log_write("[ZIP] DEVICE SUCCESS %s %s\n", path.s, entry.name); + R_UNLESS(AddDevice(&entry->devoptab) >= 0, 0x1); + log_write("[ZIP] DEVICE SUCCESS %s %s\n", path.s, entry->name); - out_path = entry.mount; - entry.ref_count++; - g_mount_idx++; + out_path = entry->mount; + entry->ref_count++; + *itr = std::move(entry); R_SUCCEED(); } @@ -664,20 +675,19 @@ void UmountZip(const fs::FsPath& mount) { SCOPED_MUTEX(&g_mutex); auto itr = std::ranges::find_if(g_entries, [&mount](auto& e){ - return mount == e.mount; + return e && e->mount == mount; }); if (itr == g_entries.end()) { return; } - if (itr->ref_count) { - itr->ref_count--; + if ((*itr)->ref_count) { + (*itr)->ref_count--; } - if (!itr->ref_count) { - RemoveDevice(mount); - g_entries.erase(itr); + if (!(*itr)->ref_count) { + itr->reset(); } }