Compare commits
20 Commits
allow_stan
...
1.2.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e256261b80 | ||
|
|
47218f0da8 | ||
|
|
409a48ec73 | ||
|
|
0c93cefd39 | ||
|
|
0bbc907907 | ||
|
|
4a4a1f0e87 | ||
|
|
705dd95375 | ||
|
|
2d5751356b | ||
|
|
b140834b7e | ||
|
|
e3d2af6b3f | ||
|
|
c17ad1e0e3 | ||
|
|
6145b3b72c | ||
|
|
aba7e4ca7d | ||
|
|
4cc5e9cdfd | ||
|
|
ca0308c7ca | ||
|
|
1d908295fe | ||
|
|
277bd101c5 | ||
|
|
3dce23773a | ||
|
|
4489513f7c | ||
|
|
5eabca7f04 |
@@ -1,4 +1,41 @@
|
||||
# Changelog
|
||||
## 1.2.4
|
||||
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
|
||||
+ Cache management (to avoid unnecessary rebuild) was revised, to add a grace period of ~500ms-1s between process closing romfs image and ams.mitm needing to rebuild if romfs is re-opened.
|
||||
+ This makes our cache much more effective, previously we were re-building romfs several times.
|
||||
+ RomFS image ownership was overhauled, with a new reference-counting implementation added (used to implement the above grace period).
|
||||
+ Certain games (e.g. Puyo Puyo Tetris 2, probably others) were sensitive to this timing, and could use access patterns which would trigger creation of romfs image while previous romfs image was in the middle of destructor.
|
||||
+ This could cause a fatal error, because the destructor for the old image could run simultaneously with building the new image.
|
||||
+ This also provides a speedup versus the 1.2.3 code, with Animal Crossing now taking ~8 fewer seconds to get past the Nintendo Switch logo.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.2.3
|
||||
+ Because ams.TMA is taking longer to develop than expected, experimental support for Atmosphère's gdbstub as a standalone is now available.
|
||||
+ To enable it, set `atmosphere!enable_standalone_gdbstub` = u8!0x1 in system_settings.ini.
|
||||
+ The standalone also requires `atmosphere!enable_htc` = u8!0x0, but this should be the case for everyone since ams.TMA isn't actually usable yet.
|
||||
+ Once enabled, open the devkitPro provided-gdb (`aarch64-none-elf-gdb` for 64-bit or `arm-none-eabi-gdb` for 32-bit).
|
||||
+ The standalone stub exposes itself on port 22225 -- so the command to connect is `target extended-remote <ip address>:22225`.
|
||||
+ Type `info os processes` to get a list of process IDs that can be attached to.
|
||||
+ The stub should work on both system programs, games, and homebrew -- but please note that debugging certain processes (like sockets) can cause hang due to the stub using them itself.
|
||||
+ Software break-points, hardware break-points, hardware watch-points, and hardware single-step are all supported/implemented.
|
||||
+ The following monitor commands are currently supported:
|
||||
+ `monitor get info`: Get process info, address space layout, and information on modules.
|
||||
+ `monitor get mappings`: Get all memory mappings.
|
||||
+ `monitor get mapping <addr>`: Get the memory mapping for a specific address.
|
||||
+ `monitor wait application`: Causes the stub to wait for an application to be launched. The next application will be started suspended.
|
||||
+ User is expected to send `attach <pid>` after launching, which will cause attach-on-first-instruction. Failure to attach may cause system instability, this probably needs work.
|
||||
+ **Please Note**: The GDBstub is new and may have bugs/need work. If you find issues, please report them to SciresM#0524 -- all help finding/fixing bugs is appreciated, here.
|
||||
+ Generally speaking, if you would like to report information about fixes needed/discuss development of the gdbstub, join ReSwitched's #dev-support channel.
|
||||
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
|
||||
+ Animal Crossing's 2.0.0 update contains >99000 files, and has tables so big that we ran out of memory even after the optimizations made in 0.10.5.
|
||||
+ Previously, we used fixed-sized 0x40000 work buffers for file/directory tables and simultaneously built hash/content tables in one loop over files/directories.
|
||||
+ We now iterate over the file/directory tables multiple times, first once to determine the hash table indices, then repeatedly to build hash tables, then once to build content tables.
|
||||
+ We also now allow smaller-than-0x40000 work buffers, trying half-as-big buffers until allocation succeeds (or work buffer would be <0x4000, which is a safeguard against truly horrible performance).
|
||||
+ There is a slight speed penalty to these changes, but it's on the order of seconds for the worst case (Animal Crossing) and trivial for most games with reasonable tables.
|
||||
+ If you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact `SciresM#0524`.
|
||||
+ It's really hard to imagine any game being worse than Animal Crossing, but if it happens again I will drop everything to fix it as usual.
|
||||
+ `creport` now attempts to parse symbol tables if present.
|
||||
+ If a game executable has a symbol for a given address, the function-relative-offset will now be printed after the module-relative-offset.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.2.2
|
||||
+ A number of fixes were made to Atmosphère's implementation of the new "sprofile" service added in 13.0.0.
|
||||
+ Nintendo is finally transmitting data over the internet to certain consoles, which has allowed for validating our service implementation.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = ceff2f3712d66904747f77619cd6dc12a5dcea8b
|
||||
parent = 6cf5205a28cfeb30930fd1cfd3c8369cd13edfa3
|
||||
commit = ff288336126aeda0444508e1af1f86c5f977c0f1
|
||||
parent = 47218f0da8c9213e6faa83ad38d63f6a00d5aff7
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace ams::impl {
|
||||
AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemFinalizeThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 2
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 4
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace ams::util {
|
||||
|
||||
constexpr size_type find(basic_string_view str, size_type pos = 0) const noexcept { return this->find(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(1)))
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find(const _CharT *str, size_type pos = 0) const noexcept { return this->find(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type rfind(const _CharT *str, size_type pos, size_type n) const noexcept {
|
||||
@@ -234,7 +234,7 @@ namespace ams::util {
|
||||
|
||||
constexpr size_type rfind(basic_string_view str, size_type pos = 0) const noexcept { return this->rfind(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(1)))
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type rfind(const _CharT *str, size_type pos = 0) const noexcept { return this->rfind(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_first_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
@@ -249,7 +249,7 @@ namespace ams::util {
|
||||
constexpr size_type find_first_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_first_of(str.m_str, pos, str.m_len); }
|
||||
constexpr size_type find_first_of(_CharT c, size_type pos = 0) const noexcept { return this->find(c, pos); }
|
||||
|
||||
__attribute__((nonnull(1)))
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_first_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_last_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
@@ -270,7 +270,7 @@ namespace ams::util {
|
||||
constexpr size_type find_last_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_last_of(str.m_str, pos, str.m_len); }
|
||||
constexpr size_type find_last_of(_CharT c, size_type pos = 0) const noexcept { return this->rfind(c, pos); }
|
||||
|
||||
__attribute__((nonnull(1)))
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_last_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_first_not_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
@@ -293,7 +293,7 @@ namespace ams::util {
|
||||
|
||||
constexpr size_type find_first_not_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_first_not_of(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(1)))
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_first_not_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_not_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_last_not_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
@@ -328,7 +328,7 @@ namespace ams::util {
|
||||
|
||||
constexpr size_type find_last_not_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_last_not_of(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(1)))
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_last_not_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_last_not_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr friend bool operator==(const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) == 0; }
|
||||
|
||||
@@ -33,44 +33,6 @@ namespace ams::mitm::fs {
|
||||
constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/";
|
||||
constexpr const char ProgramWebContentDir[] = "/manual_html/";
|
||||
|
||||
constinit os::SdkMutex g_data_storage_lock;
|
||||
constinit os::SdkMutex g_storage_cache_lock;
|
||||
|
||||
class StorageCacheEntry : public util::IntrusiveRedBlackTreeBaseNode<StorageCacheEntry> {
|
||||
public:
|
||||
using RedBlackKeyType = u64;
|
||||
private:
|
||||
ncm::ProgramId m_program_id;
|
||||
std::weak_ptr<fs::IStorage> m_storage;
|
||||
public:
|
||||
StorageCacheEntry(ncm::ProgramId program_id, const std::shared_ptr<fs::IStorage> *sp) : m_program_id(program_id), m_storage(*sp) { /* ... */ }
|
||||
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
|
||||
constexpr const std::weak_ptr<fs::IStorage> &GetStorage() const { return m_storage; }
|
||||
|
||||
void SetStorage(const std::shared_ptr<fs::IStorage> *sp) { m_storage = *sp; }
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const StorageCacheEntry &rhs) {
|
||||
const auto rval = rhs.GetProgramId().value;
|
||||
|
||||
if (lval < rval) {
|
||||
return -1;
|
||||
} else if (lval == rval) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const StorageCacheEntry &lhs, const StorageCacheEntry &rhs) {
|
||||
return Compare(lhs.GetProgramId().value, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
using StorageCache = typename util::IntrusiveRedBlackTreeBaseTraits<StorageCacheEntry>::TreeType<StorageCacheEntry>;
|
||||
|
||||
constinit StorageCache g_storage_cache;
|
||||
|
||||
constinit os::SdkMutex g_boot0_detect_lock;
|
||||
constinit bool g_detected_boot0_kind = false;
|
||||
constinit bool g_is_boot0_custom_public_key = false;
|
||||
@@ -90,30 +52,6 @@ namespace ams::mitm::fs {
|
||||
return g_is_boot0_custom_public_key;
|
||||
}
|
||||
|
||||
std::shared_ptr<fs::IStorage> GetStorageCacheEntry(ncm::ProgramId program_id) {
|
||||
std::scoped_lock lk(g_storage_cache_lock);
|
||||
|
||||
if (const auto it = g_storage_cache.find_key(program_id.value); it != g_storage_cache.end()) {
|
||||
return it->GetStorage().lock();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SetStorageCacheEntry(ncm::ProgramId program_id, std::shared_ptr<fs::IStorage> *new_intf) {
|
||||
std::scoped_lock lk(g_storage_cache_lock);
|
||||
|
||||
if (auto it = g_storage_cache.find_key(program_id.value); it != g_storage_cache.end()) {
|
||||
if (auto cur_intf = it->GetStorage().lock(); cur_intf != nullptr) {
|
||||
*new_intf = cur_intf;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto *new_entry = new StorageCacheEntry(program_id, new_intf);
|
||||
g_storage_cache.insert(*new_entry);
|
||||
}
|
||||
|
||||
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
|
||||
u8 tmp = 0;
|
||||
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValue(std::addressof(tmp), sizeof(tmp), name, key) == sizeof(tmp));
|
||||
@@ -358,38 +296,8 @@ namespace ams::mitm::fs {
|
||||
R_TRY(fsOpenDataStorageByCurrentProcessFwd(m_forward_service.get(), std::addressof(data_storage)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a scoped lock. */
|
||||
std::scoped_lock lk(g_data_storage_lock);
|
||||
|
||||
/* Try to get a storage from the cache. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(m_client_info.program_id);
|
||||
if (cached_storage != nullptr) {
|
||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a new layered romfs, and cache to storage. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> new_storage = nullptr;
|
||||
|
||||
/* Create the layered storage. */
|
||||
FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), m_client_info.program_id, "romfs.bin", OpenMode_Read))) {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), m_client_info.program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
} else {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, m_client_info.program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
}
|
||||
|
||||
SetStorageCacheEntry(m_client_info.program_id, std::addressof(new_storage));
|
||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||
}
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(m_client_info.program_id, data_storage, true)), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -403,43 +311,13 @@ namespace ams::mitm::fs {
|
||||
/* Only mitm if there is actually an override romfs. */
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the process romfs. */
|
||||
/* Try to open the data id. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageByDataIdFwd(m_forward_service.get(), std::addressof(data_storage), static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a scoped lock. */
|
||||
std::scoped_lock lk(g_data_storage_lock);
|
||||
|
||||
/* Try to get a storage from the cache. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(data_id);
|
||||
if (cached_storage != nullptr) {
|
||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a new layered romfs, and cache to storage. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> new_storage = nullptr;
|
||||
|
||||
/* Create the layered storage. */
|
||||
FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), data_id, "romfs.bin", OpenMode_Read))) {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), data_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
} else {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, data_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
}
|
||||
|
||||
SetStorageCacheEntry(data_id, std::addressof(new_storage));
|
||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||
}
|
||||
|
||||
/* Get a layered storage for the data id. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(data_id, data_storage, false)), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -459,38 +337,8 @@ namespace ams::mitm::fs {
|
||||
R_TRY(fsOpenDataStorageWithProgramIndexFwd(m_forward_service.get(), std::addressof(data_storage), program_index));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a scoped lock. */
|
||||
std::scoped_lock lk(g_data_storage_lock);
|
||||
|
||||
/* Try to get a storage from the cache. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(program_id);
|
||||
if (cached_storage != nullptr) {
|
||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a new layered romfs, and cache to storage. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> new_storage = nullptr;
|
||||
|
||||
/* Create the layered storage. */
|
||||
FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
} else {
|
||||
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
|
||||
layered_storage->BeginInitialize();
|
||||
new_storage = std::move(layered_storage);
|
||||
}
|
||||
|
||||
SetStorageCacheEntry(program_id, std::addressof(new_storage));
|
||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||
}
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(program_id, data_storage, true)), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,19 +28,121 @@ namespace ams::mitm::fs {
|
||||
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
|
||||
os::MessageQueue g_ack_mq(g_mq_storage + 1, 1);
|
||||
|
||||
class LayeredRomfsStorageHolder : public util::IntrusiveRedBlackTreeBaseNode<LayeredRomfsStorageHolder> {
|
||||
public:
|
||||
using RedBlackKeyType = u64;
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
u32 m_reference_count;
|
||||
bool m_second_chance;
|
||||
bool m_process_romfs;
|
||||
public:
|
||||
LayeredRomfsStorageHolder(LayeredRomfsStorageImpl *impl, bool process_rom) : m_impl(impl), m_reference_count(1), m_second_chance(true), m_process_romfs(process_rom) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
~LayeredRomfsStorageHolder() {
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
constexpr LayeredRomfsStorageImpl *GetImpl() const { return m_impl; }
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_impl->GetProgramId(); }
|
||||
constexpr u32 GetReferenceCount() const { return m_reference_count; }
|
||||
|
||||
void OpenReferenceImpl() { ++m_reference_count; }
|
||||
void CloseReferenceImpl() { --m_reference_count; }
|
||||
|
||||
bool GetSecondChanceImpl() const { return m_second_chance; }
|
||||
void SetSecondChanceImpl(bool sc) { m_second_chance = sc; }
|
||||
|
||||
bool IsProcessRomfs() const { return m_process_romfs; }
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const LayeredRomfsStorageHolder &rhs) {
|
||||
const auto rval = rhs.GetProgramId().value;
|
||||
|
||||
if (lval < rval) {
|
||||
return -1;
|
||||
} else if (lval == rval) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const LayeredRomfsStorageHolder &lhs, const LayeredRomfsStorageHolder &rhs) {
|
||||
return Compare(lhs.GetProgramId().value, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
using LayeredRomfsStorageSet = typename util::IntrusiveRedBlackTreeBaseTraits<LayeredRomfsStorageHolder>::TreeType<LayeredRomfsStorageHolder>;
|
||||
|
||||
constinit os::SdkRecursiveMutex g_storage_set_mutex;
|
||||
constinit LayeredRomfsStorageSet g_storage_set;
|
||||
|
||||
void OpenReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
it->OpenReferenceImpl();
|
||||
}
|
||||
|
||||
void CloseReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
AMS_ABORT_UNLESS(it->GetReferenceCount() > 0);
|
||||
it->CloseReferenceImpl();
|
||||
}
|
||||
|
||||
void RomfsInitializerThreadFunction(void *) {
|
||||
while (true) {
|
||||
uintptr_t storage_uptr = 0;
|
||||
|
||||
g_req_mq.Receive(std::addressof(storage_uptr));
|
||||
std::shared_ptr<LayeredRomfsStorage> layered_storage = reinterpret_cast<LayeredRomfsStorage *>(storage_uptr)->GetShared();
|
||||
auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
|
||||
g_ack_mq.Send(storage_uptr);
|
||||
layered_storage->InitializeImpl();
|
||||
|
||||
impl->InitializeImpl();
|
||||
|
||||
/* Close the initial reference. */
|
||||
CloseReference(impl);
|
||||
}
|
||||
}
|
||||
|
||||
void RomfsFinalizerThreadFunction(void *) {
|
||||
while (true) {
|
||||
{
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0) {
|
||||
it->SetSecondChanceImpl(true);
|
||||
++it;
|
||||
} else if (it->GetSecondChanceImpl()) {
|
||||
it->SetSecondChanceImpl(false);
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(500));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t RomfsInitializerThreadStackSize = 0x8000;
|
||||
os::ThreadType g_romfs_initializer_thread;
|
||||
os::ThreadType g_romfs_finalizer_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_initializer_thread_stack[RomfsInitializerThreadStackSize];
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_finalizer_thread_stack[os::MemoryPageSize];
|
||||
|
||||
void RequestInitializeStorage(uintptr_t storage_uptr) {
|
||||
std::scoped_lock lk(g_mq_lock);
|
||||
@@ -49,6 +151,11 @@ namespace ams::mitm::fs {
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_initializer_thread), RomfsInitializerThreadFunction, nullptr, g_romfs_initializer_thread_stack, sizeof(g_romfs_initializer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_initializer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemInitializeThread));
|
||||
os::StartThread(std::addressof(g_romfs_initializer_thread));
|
||||
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_finalizer_thread), RomfsFinalizerThreadFunction, nullptr, g_romfs_finalizer_thread_stack, sizeof(g_romfs_finalizer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_finalizer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemFinalizeThread));
|
||||
os::StartThread(std::addressof(g_romfs_finalizer_thread));
|
||||
|
||||
g_started_req_thread = true;
|
||||
}
|
||||
|
||||
@@ -58,27 +165,113 @@ namespace ams::mitm::fs {
|
||||
AMS_ABORT_UNLESS(ack == storage_uptr);
|
||||
}
|
||||
|
||||
class LayeredRomfsStorage : public ams::fs::IStorage {
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
public:
|
||||
LayeredRomfsStorage(LayeredRomfsStorageImpl *impl) : m_impl(impl) {
|
||||
OpenReference(m_impl);
|
||||
}
|
||||
|
||||
virtual ~LayeredRomfsStorage() {
|
||||
CloseReference(m_impl);
|
||||
}
|
||||
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
return m_impl->Read(offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out_size) override {
|
||||
return m_impl->GetSize(out_size);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return m_impl->Flush();
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
return m_impl->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
LayeredRomfsStorage::LayeredRomfsStorage(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
/* Find an existing storage. */
|
||||
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
|
||||
return std::make_shared<LayeredRomfsStorage>(it->GetImpl());
|
||||
}
|
||||
|
||||
/* We don't have an existing storage. If we're creating process romfs, free any unreferenced process romfs. */
|
||||
/* This should help prevent too much memory in use at any time. */
|
||||
if (is_process_romfs) {
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0 || !it->IsProcessRomfs()) {
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new storage. */
|
||||
LayeredRomfsStorageImpl *impl;
|
||||
{
|
||||
::FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
|
||||
} else {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert holder. Reference count will now be one. */
|
||||
g_storage_set.insert(*(new LayeredRomfsStorageHolder(impl, is_process_romfs)));
|
||||
|
||||
/* Begin initialization. When this finishes, a decref will occur. */
|
||||
impl->BeginInitialize();
|
||||
|
||||
/* Return a new shared storage for the impl. */
|
||||
return std::make_shared<LayeredRomfsStorage>(impl);
|
||||
}
|
||||
|
||||
LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
LayeredRomfsStorage::~LayeredRomfsStorage() {
|
||||
LayeredRomfsStorageImpl::~LayeredRomfsStorageImpl() {
|
||||
for (size_t i = 0; i < m_source_infos.size(); i++) {
|
||||
m_source_infos[i].Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void LayeredRomfsStorage::BeginInitialize() {
|
||||
void LayeredRomfsStorageImpl::BeginInitialize() {
|
||||
AMS_ABORT_UNLESS(!m_started_initialize);
|
||||
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
|
||||
m_started_initialize = true;
|
||||
}
|
||||
|
||||
void LayeredRomfsStorage::InitializeImpl() {
|
||||
void LayeredRomfsStorageImpl::InitializeImpl() {
|
||||
/* Build new virtual romfs. */
|
||||
romfs::Builder builder(m_program_id);
|
||||
|
||||
@@ -98,7 +291,7 @@ namespace ams::mitm::fs {
|
||||
m_initialize_event.Signal();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::Read(s64 offset, void *buffer, size_t size) {
|
||||
Result LayeredRomfsStorageImpl::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Check if we can succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
@@ -178,7 +371,7 @@ namespace ams::mitm::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::GetSize(s64 *out_size) {
|
||||
Result LayeredRomfsStorageImpl::GetSize(s64 *out_size) {
|
||||
/* Ensure we're initialized. */
|
||||
if (!m_is_initialized) {
|
||||
m_initialize_event.Wait();
|
||||
@@ -188,11 +381,11 @@ namespace ams::mitm::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::Flush() {
|
||||
Result LayeredRomfsStorageImpl::Flush() {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorage::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
Result LayeredRomfsStorageImpl::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
AMS_UNUSED(offset, src, src_size);
|
||||
|
||||
switch (op_id) {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class LayeredRomfsStorage : public std::enable_shared_from_this<LayeredRomfsStorage>, public ams::fs::IStorage {
|
||||
class LayeredRomfsStorageImpl {
|
||||
private:
|
||||
std::vector<romfs::SourceInfo> m_source_infos;
|
||||
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
|
||||
@@ -35,32 +35,20 @@ namespace ams::mitm::fs {
|
||||
return back.virtual_offset + back.size;
|
||||
}
|
||||
public:
|
||||
LayeredRomfsStorage(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
||||
virtual ~LayeredRomfsStorage();
|
||||
LayeredRomfsStorageImpl(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
||||
~LayeredRomfsStorageImpl();
|
||||
|
||||
void BeginInitialize();
|
||||
void InitializeImpl();
|
||||
|
||||
std::shared_ptr<LayeredRomfsStorage> GetShared() {
|
||||
return this->shared_from_this();
|
||||
}
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result GetSize(s64 *out_size) override;
|
||||
virtual Result Flush() override;
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(size);
|
||||
return ams::fs::ResultUnsupportedOperation();
|
||||
}
|
||||
Result Read(s64 offset, void *buffer, size_t size);
|
||||
Result GetSize(s64 *out_size);
|
||||
Result Flush();
|
||||
Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size);
|
||||
};
|
||||
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
|
||||
|
||||
}
|
||||
|
||||
@@ -64,28 +64,61 @@ namespace ams::mitm::fs {
|
||||
};
|
||||
static_assert(util::is_pod<FileEntry>::value && sizeof(FileEntry) == 0x20);
|
||||
|
||||
class DynamicTableCache {
|
||||
NON_COPYABLE(DynamicTableCache);
|
||||
NON_MOVEABLE(DynamicTableCache);
|
||||
private:
|
||||
static constexpr size_t MaxCachedSize = (1_MB / 4);
|
||||
private:
|
||||
size_t m_cache_bitsize;
|
||||
protected:
|
||||
void *m_cache;
|
||||
protected:
|
||||
DynamicTableCache(size_t sz) {
|
||||
size_t cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize));
|
||||
m_cache = std::malloc(cache_size);
|
||||
while (m_cache == nullptr) {
|
||||
cache_size >>= 1;
|
||||
AMS_ABORT_UNLESS(cache_size >= 16_KB);
|
||||
}
|
||||
m_cache_bitsize = util::CountTrailingZeros(cache_size);
|
||||
}
|
||||
|
||||
~DynamicTableCache() {
|
||||
std::free(m_cache);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE size_t GetCacheSize() const { return static_cast<size_t>(1) << m_cache_bitsize; }
|
||||
};
|
||||
|
||||
class HashTableStorage : public DynamicTableCache {
|
||||
public:
|
||||
HashTableStorage(size_t sz) : DynamicTableCache(sz) { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE u32 *GetBuffer() { return reinterpret_cast<u32 *>(m_cache); }
|
||||
ALWAYS_INLINE size_t GetBufferSize() const { return DynamicTableCache::GetCacheSize(); }
|
||||
};
|
||||
|
||||
template<typename Entry>
|
||||
class TableReader {
|
||||
class TableReader : public DynamicTableCache {
|
||||
NON_COPYABLE(TableReader);
|
||||
NON_MOVEABLE(TableReader);
|
||||
private:
|
||||
static constexpr size_t MaxCachedSize = (1_MB / 4);
|
||||
static constexpr size_t FallbackCacheSize = 1_KB;
|
||||
private:
|
||||
ams::fs::IStorage *m_storage;
|
||||
size_t m_offset;
|
||||
size_t m_size;
|
||||
size_t m_cache_idx;
|
||||
void *m_cache;
|
||||
u8 m_fallback_cache[FallbackCacheSize];
|
||||
private:
|
||||
ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) {
|
||||
R_ABORT_UNLESS(m_storage->Read(m_offset + ofs, dst, size));
|
||||
}
|
||||
ALWAYS_INLINE void ReloadCacheImpl(size_t idx) {
|
||||
const size_t rel_ofs = idx * MaxCachedSize;
|
||||
const size_t rel_ofs = idx * this->GetCacheSize();
|
||||
AMS_ABORT_UNLESS(rel_ofs < m_size);
|
||||
const size_t new_cache_size = std::min(m_size - rel_ofs, MaxCachedSize);
|
||||
const size_t new_cache_size = std::min(m_size - rel_ofs, this->GetCacheSize());
|
||||
this->Read(rel_ofs, m_cache, new_cache_size);
|
||||
m_cache_idx = idx;
|
||||
}
|
||||
@@ -97,23 +130,18 @@ namespace ams::mitm::fs {
|
||||
}
|
||||
|
||||
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
|
||||
return ofs / MaxCachedSize;
|
||||
return ofs / this->GetCacheSize();
|
||||
}
|
||||
public:
|
||||
TableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : m_storage(s), m_offset(ofs), m_size(sz), m_cache_idx(0) {
|
||||
m_cache = std::malloc(std::min(sz, MaxCachedSize));
|
||||
TableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : DynamicTableCache(sz), m_storage(s), m_offset(ofs), m_size(sz), m_cache_idx(0) {
|
||||
AMS_ABORT_UNLESS(m_cache != nullptr);
|
||||
this->ReloadCacheImpl(0);
|
||||
}
|
||||
|
||||
~TableReader() {
|
||||
std::free(m_cache);
|
||||
}
|
||||
|
||||
const Entry *GetEntry(u32 entry_offset) {
|
||||
this->ReloadCache(this->GetCacheIndex(entry_offset));
|
||||
|
||||
const size_t ofs = entry_offset % MaxCachedSize;
|
||||
const size_t ofs = entry_offset % this->GetCacheSize();
|
||||
|
||||
const Entry *entry = reinterpret_cast<const Entry *>(reinterpret_cast<uintptr_t>(m_cache) + ofs);
|
||||
if (AMS_UNLIKELY(this->GetCacheIndex(entry_offset) != this->GetCacheIndex(entry_offset + sizeof(Entry) + entry->name_size + sizeof(u32)))) {
|
||||
@@ -125,18 +153,16 @@ namespace ams::mitm::fs {
|
||||
};
|
||||
|
||||
template<typename Entry>
|
||||
class TableWriter {
|
||||
class TableWriter : public DynamicTableCache {
|
||||
NON_COPYABLE(TableWriter);
|
||||
NON_MOVEABLE(TableWriter);
|
||||
private:
|
||||
static constexpr size_t MaxCachedSize = (1_MB / 4);
|
||||
static constexpr size_t FallbackCacheSize = 1_KB;
|
||||
private:
|
||||
::FsFile *m_file;
|
||||
size_t m_offset;
|
||||
size_t m_size;
|
||||
size_t m_cache_idx;
|
||||
void *m_cache;
|
||||
u8 m_fallback_cache[FallbackCacheSize];
|
||||
size_t m_fallback_cache_entry_offset;
|
||||
size_t m_fallback_cache_entry_size;
|
||||
@@ -157,8 +183,8 @@ namespace ams::mitm::fs {
|
||||
AMS_ABORT_UNLESS(!(m_cache_dirty && m_fallback_cache_dirty));
|
||||
|
||||
if (m_cache_dirty) {
|
||||
const size_t ofs = m_cache_idx * MaxCachedSize;
|
||||
this->Write(ofs, m_cache, std::min(m_size - ofs, MaxCachedSize));
|
||||
const size_t ofs = m_cache_idx * this->GetCacheSize();
|
||||
this->Write(ofs, m_cache, std::min(m_size - ofs, this->GetCacheSize()));
|
||||
m_cache_dirty = false;
|
||||
}
|
||||
if (m_fallback_cache_dirty) {
|
||||
@@ -168,12 +194,12 @@ namespace ams::mitm::fs {
|
||||
}
|
||||
|
||||
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
|
||||
return ofs / MaxCachedSize;
|
||||
return ofs / this->GetCacheSize();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void RefreshCacheImpl() {
|
||||
const size_t cur_cache = m_cache_idx * MaxCachedSize;
|
||||
this->Read(cur_cache, m_cache, std::min(m_size - cur_cache, MaxCachedSize));
|
||||
const size_t cur_cache = m_cache_idx * this->GetCacheSize();
|
||||
this->Read(cur_cache, m_cache, std::min(m_size - cur_cache, this->GetCacheSize()));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void RefreshCache(u32 entry_offset) {
|
||||
@@ -184,29 +210,27 @@ namespace ams::mitm::fs {
|
||||
}
|
||||
}
|
||||
public:
|
||||
TableWriter(::FsFile *f, size_t ofs, size_t sz) : m_file(f), m_offset(ofs), m_size(sz), m_cache_idx(0), m_fallback_cache_entry_offset(), m_fallback_cache_entry_size(), m_cache_dirty(), m_fallback_cache_dirty() {
|
||||
const size_t cache_size = std::min(sz, MaxCachedSize);
|
||||
m_cache = std::malloc(cache_size);
|
||||
TableWriter(::FsFile *f, size_t ofs, size_t sz) : DynamicTableCache(sz), m_file(f), m_offset(ofs), m_size(sz), m_cache_idx(0), m_fallback_cache_entry_offset(), m_fallback_cache_entry_size(), m_cache_dirty(), m_fallback_cache_dirty() {
|
||||
AMS_ABORT_UNLESS(m_cache != nullptr);
|
||||
std::memset(m_cache, 0, cache_size);
|
||||
|
||||
std::memset(m_cache, 0, this->GetCacheSize());
|
||||
std::memset(m_fallback_cache, 0, sizeof(m_fallback_cache));
|
||||
for (size_t cur = 0; cur < m_size; cur += MaxCachedSize) {
|
||||
this->Write(cur, m_cache, std::min(m_size - cur, MaxCachedSize));
|
||||
for (size_t cur = 0; cur < m_size; cur += this->GetCacheSize()) {
|
||||
this->Write(cur, m_cache, std::min(m_size - cur, this->GetCacheSize()));
|
||||
}
|
||||
}
|
||||
|
||||
~TableWriter() {
|
||||
this->Flush();
|
||||
std::free(m_cache);
|
||||
}
|
||||
|
||||
Entry *GetEntry(u32 entry_offset, u32 name_len) {
|
||||
this->RefreshCache(entry_offset);
|
||||
|
||||
const size_t ofs = entry_offset % MaxCachedSize;
|
||||
const size_t ofs = entry_offset % this->GetCacheSize();
|
||||
|
||||
Entry *entry = reinterpret_cast<Entry *>(reinterpret_cast<uintptr_t>(m_cache) + ofs);
|
||||
if (ofs + sizeof(Entry) + util::AlignUp(name_len, sizeof(u32)) > MaxCachedSize) {
|
||||
if (ofs + sizeof(Entry) + util::AlignUp(name_len, sizeof(u32)) > this->GetCacheSize()) {
|
||||
this->Flush();
|
||||
|
||||
m_fallback_cache_entry_offset = entry_offset;
|
||||
@@ -226,7 +250,6 @@ namespace ams::mitm::fs {
|
||||
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
|
||||
using FileTableWriter = TableWriter<FileEntry>;
|
||||
|
||||
|
||||
constexpr inline u32 CalculatePathHash(u32 parent, const char *_path, u32 start, size_t path_len) {
|
||||
const unsigned char *path = reinterpret_cast<const unsigned char *>(_path);
|
||||
u32 hash = parent ^ 123456789;
|
||||
@@ -526,113 +549,151 @@ namespace ams::mitm::fs {
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate file tables. */
|
||||
{
|
||||
/* Allocate the hash table. */
|
||||
void *fht_buf = std::malloc(m_file_hash_table_size);
|
||||
AMS_ABORT_UNLESS(fht_buf != nullptr);
|
||||
u32 *file_hash_table = reinterpret_cast<u32 *>(fht_buf);
|
||||
std::memset(file_hash_table, 0xFF, m_file_hash_table_size);
|
||||
ON_SCOPE_EXIT {
|
||||
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size, file_hash_table, m_file_hash_table_size, FsWriteOption_None));
|
||||
std::free(fht_buf);
|
||||
};
|
||||
/* Set all files' hash value = hash index. */
|
||||
for (const auto &it : m_files) {
|
||||
BuildFileContext *cur_file = it.get();
|
||||
cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path.get(), 0, cur_file->path_len) % num_file_hash_table_entries;
|
||||
}
|
||||
|
||||
/* Write the file table. */
|
||||
{
|
||||
FileTableWriter file_table(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size + m_file_hash_table_size, m_file_table_size);
|
||||
/* Set all directories' hash value = hash index. */
|
||||
for (const auto &it : m_directories) {
|
||||
BuildDirectoryContext *cur_dir = it.get();
|
||||
cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path.get(), 0, cur_dir->path_len) % num_dir_hash_table_entries;
|
||||
}
|
||||
|
||||
/* Write hash tables. */
|
||||
{
|
||||
HashTableStorage hash_table_storage(std::max(m_dir_hash_table_size, m_file_hash_table_size));
|
||||
|
||||
u32 *hash_table = hash_table_storage.GetBuffer();
|
||||
size_t hash_table_size = hash_table_storage.GetBufferSize();
|
||||
|
||||
/* Write the file hash table. */
|
||||
for (size_t ofs = 0; ofs < m_file_hash_table_size; ofs += hash_table_size) {
|
||||
std::memset(hash_table, 0xFF, hash_table_size);
|
||||
|
||||
const u32 ofs_ind = ofs / sizeof(u32);
|
||||
const u32 end_ind = (ofs + hash_table_size) / sizeof(u32);
|
||||
|
||||
for (const auto &it : m_files) {
|
||||
BuildFileContext *cur_file = it.get();
|
||||
FileEntry *cur_entry = file_table.GetEntry(cur_file->entry_offset, cur_file->path_len);
|
||||
if (cur_file->HasHashMark()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Set entry fields. */
|
||||
cur_entry->parent = cur_file->parent->entry_offset;
|
||||
cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset;
|
||||
cur_entry->offset = cur_file->offset;
|
||||
cur_entry->size = cur_file->size;
|
||||
if (const auto hash_ind = cur_file->hash_value; ofs_ind <= hash_ind && hash_ind < end_ind) {
|
||||
cur_file->hash_value = hash_table[hash_ind - ofs_ind];
|
||||
hash_table[hash_ind - ofs_ind] = cur_file->entry_offset;
|
||||
|
||||
/* Insert into hash table. */
|
||||
const u32 name_size = cur_file->path_len;
|
||||
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_file->path.get(), 0, name_size) % num_file_hash_table_entries;
|
||||
cur_entry->hash = file_hash_table[hash_ind];
|
||||
file_hash_table[hash_ind] = cur_file->entry_offset;
|
||||
cur_file->SetHashMark();
|
||||
}
|
||||
}
|
||||
|
||||
/* Set name. */
|
||||
cur_entry->name_size = name_size;
|
||||
if (name_size) {
|
||||
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
|
||||
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
|
||||
cur_entry->name[i] = 0;
|
||||
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size + ofs, hash_table, std::min(m_file_hash_table_size - ofs, hash_table_size), FsWriteOption_None));
|
||||
}
|
||||
|
||||
/* Write the directory hash table. */
|
||||
for (size_t ofs = 0; ofs < m_dir_hash_table_size; ofs += hash_table_size) {
|
||||
std::memset(hash_table, 0xFF, hash_table_size);
|
||||
|
||||
const u32 ofs_ind = ofs / sizeof(u32);
|
||||
const u32 end_ind = (ofs + hash_table_size) / sizeof(u32);
|
||||
|
||||
for (const auto &it : m_directories) {
|
||||
BuildDirectoryContext *cur_dir = it.get();
|
||||
if (cur_dir->HasHashMark()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto hash_ind = cur_dir->hash_value; ofs_ind <= hash_ind && hash_ind < end_ind) {
|
||||
cur_dir->hash_value = hash_table[hash_ind - ofs_ind];
|
||||
hash_table[hash_ind - ofs_ind] = cur_dir->entry_offset;
|
||||
|
||||
cur_dir->SetHashMark();
|
||||
}
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), ofs, hash_table, std::min(m_dir_hash_table_size - ofs, hash_table_size), FsWriteOption_None));
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the file table. */
|
||||
{
|
||||
FileTableWriter file_table(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size + m_file_hash_table_size, m_file_table_size);
|
||||
|
||||
for (const auto &it : m_files) {
|
||||
BuildFileContext *cur_file = it.get();
|
||||
FileEntry *cur_entry = file_table.GetEntry(cur_file->entry_offset, cur_file->path_len);
|
||||
|
||||
cur_file->ClearHashMark();
|
||||
|
||||
/* Set entry fields. */
|
||||
cur_entry->parent = cur_file->parent->entry_offset;
|
||||
cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset;
|
||||
cur_entry->offset = cur_file->offset;
|
||||
cur_entry->size = cur_file->size;
|
||||
cur_entry->hash = cur_file->hash_value;
|
||||
|
||||
/* Set name. */
|
||||
const u32 name_size = cur_file->path_len;
|
||||
cur_entry->name_size = name_size;
|
||||
if (name_size) {
|
||||
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
|
||||
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
|
||||
cur_entry->name[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Emplace a source. */
|
||||
switch (cur_file->source_type) {
|
||||
case DataSourceType::Storage:
|
||||
case DataSourceType::File:
|
||||
{
|
||||
/* Try to compact if possible. */
|
||||
auto &back = out_infos->back();
|
||||
if (back.source_type == cur_file->source_type) {
|
||||
back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset;
|
||||
} else {
|
||||
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Emplace a source. */
|
||||
switch (cur_file->source_type) {
|
||||
case DataSourceType::Storage:
|
||||
case DataSourceType::File:
|
||||
{
|
||||
/* Try to compact if possible. */
|
||||
auto &back = out_infos->back();
|
||||
if (back.source_type == cur_file->source_type) {
|
||||
back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset;
|
||||
} else {
|
||||
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataSourceType::LooseSdFile:
|
||||
{
|
||||
char *new_path = new char[cur_file->GetPathLength() + 1];
|
||||
cur_file->GetPath(new_path);
|
||||
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
break;
|
||||
case DataSourceType::LooseSdFile:
|
||||
{
|
||||
char *new_path = new char[cur_file->GetPathLength() + 1];
|
||||
cur_file->GetPath(new_path);
|
||||
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate directory tables. */
|
||||
/* Write the directory table. */
|
||||
{
|
||||
/* Allocate the hash table. */
|
||||
void *dht_buf = std::malloc(m_dir_hash_table_size);
|
||||
AMS_ABORT_UNLESS(dht_buf != nullptr);
|
||||
u32 *dir_hash_table = reinterpret_cast<u32 *>(dht_buf);
|
||||
std::memset(dir_hash_table, 0xFF, m_dir_hash_table_size);
|
||||
ON_SCOPE_EXIT {
|
||||
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), 0, dir_hash_table, m_dir_hash_table_size, FsWriteOption_None));
|
||||
std::free(dht_buf);
|
||||
};
|
||||
DirectoryTableWriter dir_table(std::addressof(metadata_file), m_dir_hash_table_size, m_dir_table_size);
|
||||
|
||||
/* Write the file table. */
|
||||
{
|
||||
DirectoryTableWriter dir_table(std::addressof(metadata_file), m_dir_hash_table_size, m_dir_table_size);
|
||||
for (const auto &it : m_directories) {
|
||||
BuildDirectoryContext *cur_dir = it.get();
|
||||
DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len);
|
||||
|
||||
for (const auto &it : m_directories) {
|
||||
BuildDirectoryContext *cur_dir = it.get();
|
||||
DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len);
|
||||
cur_dir->ClearHashMark();
|
||||
|
||||
/* Set entry fields. */
|
||||
cur_entry->parent = cur_dir == m_root ? 0 : cur_dir->parent->entry_offset;
|
||||
cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset;
|
||||
cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset;
|
||||
cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset;
|
||||
/* Set entry fields. */
|
||||
cur_entry->parent = cur_dir == m_root ? 0 : cur_dir->parent->entry_offset;
|
||||
cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset;
|
||||
cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset;
|
||||
cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset;
|
||||
cur_entry->hash = cur_dir->hash_value;
|
||||
|
||||
/* Insert into hash table. */
|
||||
const u32 name_size = cur_dir->path_len;
|
||||
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_dir->path.get(), 0, name_size) % num_dir_hash_table_entries;
|
||||
cur_entry->hash = dir_hash_table[hash_ind];
|
||||
dir_hash_table[hash_ind] = cur_dir->entry_offset;
|
||||
|
||||
/* Set name. */
|
||||
cur_entry->name_size = name_size;
|
||||
if (name_size) {
|
||||
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
|
||||
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
|
||||
cur_entry->name[i] = 0;
|
||||
}
|
||||
/* Set name. */
|
||||
const u32 name_size = cur_dir->path_len;
|
||||
cur_entry->name_size = name_size;
|
||||
if (name_size) {
|
||||
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
|
||||
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
|
||||
cur_entry->name[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dns_mitm/dnsmitm_debug.hpp"
|
||||
|
||||
namespace ams::mitm::fs::romfs {
|
||||
|
||||
enum class DataSourceType {
|
||||
enum class DataSourceType : u8 {
|
||||
Storage,
|
||||
File,
|
||||
LooseSdFile,
|
||||
@@ -120,10 +121,11 @@ namespace ams::mitm::fs::romfs {
|
||||
BuildFileContext *file;
|
||||
u32 path_len;
|
||||
u32 entry_offset;
|
||||
u32 hash_value;
|
||||
|
||||
struct RootTag{};
|
||||
|
||||
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0) {
|
||||
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
|
||||
this->path = std::make_unique<char[]>(1);
|
||||
}
|
||||
|
||||
@@ -154,6 +156,18 @@ namespace ams::mitm::fs::romfs {
|
||||
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||
return parent_len + 1 + this->path_len;
|
||||
}
|
||||
|
||||
bool HasHashMark() const {
|
||||
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
void SetHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
|
||||
}
|
||||
|
||||
void ClearHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
|
||||
}
|
||||
};
|
||||
|
||||
struct BuildFileContext {
|
||||
@@ -168,9 +182,10 @@ namespace ams::mitm::fs::romfs {
|
||||
s64 orig_offset;
|
||||
u32 path_len;
|
||||
u32 entry_offset;
|
||||
u32 hash_value;
|
||||
DataSourceType source_type;
|
||||
|
||||
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), source_type(type) {
|
||||
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
|
||||
this->path_len = entry_name_len;
|
||||
this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
|
||||
std::memcpy(this->path.get(), entry_name, entry_name_len);
|
||||
@@ -197,6 +212,18 @@ namespace ams::mitm::fs::romfs {
|
||||
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||
return parent_len + 1 + this->path_len;
|
||||
}
|
||||
|
||||
bool HasHashMark() const {
|
||||
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
void SetHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
|
||||
}
|
||||
|
||||
void ClearHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
|
||||
}
|
||||
};
|
||||
|
||||
class DirectoryTableReader;
|
||||
|
||||
Reference in New Issue
Block a user