Compare commits

..

18 Commits

Author SHA1 Message Date
Michael Scire
409a48ec73 docs: add changelog for 1.2.3 2021-11-04 00:23:23 -07:00
Michael Scire
0c93cefd39 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "e7b84767c"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "e7b84767c"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-11-04 00:00:26 -07:00
Michael Scire
0bbc907907 git subrepo pull (merge) libraries
subrepo:
  subdir:   "libraries"
  merged:   "8764bd406"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "12d0ba172"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-11-03 23:59:17 -07:00
Michael Scire
4a4a1f0e87 ams: bump api version to 1.2.3 2021-11-03 23:57:30 -07:00
Michael Scire
705dd95375 dmnt2: fix gdb register output 2021-11-03 23:56:25 -07:00
Michael Scire
2d5751356b creport: fix errant ) 2021-11-03 23:56:25 -07:00
Michael Scire
b140834b7e dmnt2: try to avoid writing out of bounds when generating packets 2021-11-03 23:56:25 -07:00
Michael Scire
e3d2af6b3f dmnt2: fix module name detection, add auto-break on hb nro launch 2021-11-03 23:56:25 -07:00
jam1garner
c17ad1e0e3 dmnt2: fix missing null-terminator for invalid command error 2021-11-03 23:56:25 -07:00
Michael Scire
6145b3b72c dmnt2: detect thread name, add monitor get mapping(s), increase buffer sizes 2021-11-03 23:56:25 -07:00
Michael Scire
aba7e4ca7d dmnt2: remove memory-map output which does nothing for us 2021-11-03 23:56:25 -07:00
Michael Scire
4cc5e9cdfd kern/dmnt2: allow retrieval of process info via extension
This also fixes ctrl-c break in gdbstub, and fixes crash on unknown monitor cmd.
2021-11-03 23:56:25 -07:00
Michael Scire
ca0308c7ca dmnt2: first pass at wait-for-application 2021-11-03 23:56:25 -07:00
Michael Scire
1d908295fe dmnt2: add monitor get base, TODO responses for monitor wait * 2021-11-03 23:56:25 -07:00
Michael Scire
277bd101c5 dmnt2: add memory-map read, improve module shared-lib names 2021-11-03 23:56:25 -07:00
Michael Scire
3dce23773a Adding setting usage to dmnt2 means dmnt2 needs settings access 2021-11-03 23:56:25 -07:00
Michael Scire
4489513f7c dmnt: enable experimental standalone usage of gdbstub, while starlink is in dev 2021-11-03 23:56:25 -07:00
Michael Scire
5eabca7f04 ams.mitm: more romfs building space/time tradeoffs.
This is needed for Animal Crossing 2.0.0, which has >99000 fucking files.

We now do several passes over dir/file tables instead of one pass,
doing entire hash tables before we touch dir/file tables. Thus we
no longer need to simultaneously allocate hash table and dir/file table space.

In addition, we now do repeated passes building a segment of hash tables
at a time, when insufficient memory is available. Similar is also now the
case for file/dir tables, we try 0x40000 work buffer and divide by 2
until we successfully alloc. We don't allow a work buffer <0x4000, for
write/perf reasons. If a game triggers that, let me know I guess.

Hard to imagine a worse torture-test for this code than animal crossing.
2021-11-03 23:52:38 -07:00
6 changed files with 248 additions and 133 deletions

View File

@@ -1,4 +1,32 @@
# Changelog
## 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.

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = ceff2f3712d66904747f77619cd6dc12a5dcea8b
parent = 6cf5205a28cfeb30930fd1cfd3c8369cd13edfa3
commit = e7b84767c6f3dd4c2a97fa00201342c237bf67b4
parent = 0bbc907907f909f67c1ba1ce89f8efb3b79b704a
method = merge
cmdver = 0.4.1

View File

@@ -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 3
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO

View File

@@ -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; }

View File

@@ -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;
}
}
}

View File

@@ -19,7 +19,7 @@
namespace ams::mitm::fs::romfs {
enum class DataSourceType {
enum class DataSourceType : u8 {
Storage,
File,
LooseSdFile,
@@ -120,10 +120,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 +155,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 +181,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 +211,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;