From 13e2d534e9dc9ec6208df09aa91fb272003844a4 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sun, 9 Feb 2020 20:59:31 +0800 Subject: [PATCH] core: Add dump RomFS support This is added to LayeredFS, then the NCCH container and then the loader interface. --- src/core/file_sys/layered_fs.cpp | 64 ++++++++++++++++++++++++++-- src/core/file_sys/layered_fs.h | 8 +++- src/core/file_sys/ncch_container.cpp | 22 +++++++++- src/core/file_sys/ncch_container.h | 10 ++++- src/core/loader/loader.h | 18 ++++++++ src/core/loader/ncch.cpp | 12 ++++++ src/core/loader/ncch.h | 4 ++ 7 files changed, 131 insertions(+), 7 deletions(-) diff --git a/src/core/file_sys/layered_fs.cpp b/src/core/file_sys/layered_fs.cpp index 65ececd31..8cfe73846 100644 --- a/src/core/file_sys/layered_fs.cpp +++ b/src/core/file_sys/layered_fs.cpp @@ -52,7 +52,7 @@ struct FileMetadata { static_assert(sizeof(FileMetadata) == 0x20, "Size of FileMetadata is not correct"); LayeredFS::LayeredFS(std::shared_ptr romfs_, std::string patch_path_, - std::string patch_ext_path_) + std::string patch_ext_path_, bool load_relocations) : romfs(std::move(romfs_)), patch_path(std::move(patch_path_)), patch_ext_path(std::move(patch_ext_path_)) { @@ -64,8 +64,10 @@ LayeredFS::LayeredFS(std::shared_ptr romfs_, std::string patch_path root.parent = &root; LoadDirectory(root, 0); - LoadRelocations(); - LoadExtRelocations(); + if (load_relocations) { + LoadRelocations(); + LoadExtRelocations(); + } RebuildMetadata(); } @@ -541,4 +543,60 @@ std::size_t LayeredFS::ReadFile(std::size_t offset, std::size_t length, u8* buff return read_size; } +bool LayeredFS::ExtractDirectory(Directory& current, const std::string& target_path) { + if (!FileUtil::CreateFullPath(target_path + current.path)) { + LOG_ERROR(Service_FS, "Could not create path {}", target_path + current.path); + return false; + } + + constexpr std::size_t BufferSize = 0x10000; + std::array buffer; + for (const auto& file : current.files) { + // Extract file + const auto path = target_path + file->path; + LOG_INFO(Service_FS, "Extracting {} to {}", file->path, path); + + FileUtil::IOFile target_file(path, "wb"); + if (!target_file) { + LOG_ERROR(Service_FS, "Could not open file {}", path); + return false; + } + + std::size_t written = 0; + while (written < file->relocation.size) { + const auto to_read = + std::min(buffer.size(), file->relocation.size - written); + if (romfs->ReadFile(file->relocation.original_offset + written, to_read, + buffer.data()) != to_read) { + LOG_ERROR(Service_FS, "Could not read from RomFS"); + return false; + } + + if (target_file.WriteBytes(buffer.data(), to_read) != to_read) { + LOG_ERROR(Service_FS, "Could not write to file {}", path); + return false; + } + + written += to_read; + } + } + + for (const auto& directory : current.directories) { + if (!ExtractDirectory(*directory, target_path)) { + return false; + } + } + + return true; +} + +bool LayeredFS::DumpRomFS(const std::string& target_path) { + std::string path = target_path; + if (path.back() == '/' || path.back() == '\\') { + path.erase(path.size() - 1, 1); + } + + return ExtractDirectory(root, path); +} + } // namespace FileSys diff --git a/src/core/file_sys/layered_fs.h b/src/core/file_sys/layered_fs.h index 4f8844d98..956eedcfa 100644 --- a/src/core/file_sys/layered_fs.h +++ b/src/core/file_sys/layered_fs.h @@ -41,12 +41,14 @@ static_assert(sizeof(RomFSHeader) == 0x28, "Size of RomFSHeader is not correct") class LayeredFS : public RomFSReader { public: explicit LayeredFS(std::shared_ptr romfs, std::string patch_path, - std::string patch_ext_path); + std::string patch_ext_path, bool load_relocations = true); ~LayeredFS() override; std::size_t GetSize() const override; std::size_t ReadFile(std::size_t offset, std::size_t length, u8* buffer) override; + bool DumpRomFS(const std::string& target_path); + private: struct File; struct Directory { @@ -84,6 +86,10 @@ private: void BuildDirectories(); void BuildFiles(); + // Recursively extract a directory and all its contents to target_path + // target_path should be without trailing '/'. + bool ExtractDirectory(Directory& current, const std::string& target_path); + void RebuildMetadata(); std::shared_ptr romfs; diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index 11be43c0e..2e549a894 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -597,7 +597,8 @@ Loader::ResultStatus NCCHContainer::LoadOverrideExeFSSection(const char* name, return Loader::ResultStatus::ErrorNotUsed; } -Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& romfs_file) { +Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& romfs_file, + bool use_layered_fs) { Loader::ResultStatus result = Load(); if (result != Loader::ResultStatus::Success) return result; @@ -640,7 +641,9 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& romf const auto path = fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), ncch_header.program_id & 0x00040000'FFFFFFFF); - if (FileUtil::Exists(path + "romfs/") || FileUtil::Exists(path + "romfs_ext/")) { + if (use_layered_fs && + (FileUtil::Exists(path + "romfs/") || FileUtil::Exists(path + "romfs_ext/"))) { + romfs_file = std::make_shared(std::move(direct_romfs), path + "romfs/", path + "romfs_ext/"); } else { @@ -650,6 +653,21 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& romf return Loader::ResultStatus::Success; } +Loader::ResultStatus NCCHContainer::DumpRomFS(const std::string& target_path) { + std::shared_ptr direct_romfs; + Loader::ResultStatus result = ReadRomFS(direct_romfs, false); + if (result != Loader::ResultStatus::Success) + return result; + + std::shared_ptr layered_fs = + std::make_shared(std::move(direct_romfs), "", "", false); + + if (!layered_fs->DumpRomFS(target_path)) { + return Loader::ResultStatus::Error; + } + return Loader::ResultStatus::Success; +} + Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr& romfs_file) { // Check for RomFS overrides std::string split_filepath = filepath + ".romfs"; diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h index f06ee8ef6..8deda1fff 100644 --- a/src/core/file_sys/ncch_container.h +++ b/src/core/file_sys/ncch_container.h @@ -247,7 +247,15 @@ public: * @param size The size of the romfs * @return ResultStatus result of function */ - Loader::ResultStatus ReadRomFS(std::shared_ptr& romfs_file); + Loader::ResultStatus ReadRomFS(std::shared_ptr& romfs_file, + bool use_layered_fs = true); + + /** + * Dump the RomFS of the NCCH container to the user folder. + * @param target_path target path to dump to + * @return ResultStatus result of function. + */ + Loader::ResultStatus DumpRomFS(const std::string& target_path); /** * Get the override RomFS of the NCCH container diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 20e84c6a9..0414f181c 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -186,6 +186,15 @@ public: return ResultStatus::ErrorNotImplemented; } + /** + * Dump the RomFS of the applciation + * @param target_path The target path to dump to + * @return ResultStatus result of function + */ + virtual ResultStatus DumpRomFS(const std::string& target_path) { + return ResultStatus::ErrorNotImplemented; + } + /** * Get the update RomFS of the application * Since the RomFS can be huge, we return a file reference instead of copying to a buffer @@ -196,6 +205,15 @@ public: return ResultStatus::ErrorNotImplemented; } + /** + * Dump the update RomFS of the applciation + * @param target_path The target path to dump to + * @return ResultStatus result of function + */ + virtual ResultStatus DumpUpdateRomFS(const std::string& target_path) { + return ResultStatus::ErrorNotImplemented; + } + /** * Get the title of the application * @param title Reference to store the application title into diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 2e688e011..21e607ad5 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -254,6 +254,18 @@ ResultStatus AppLoader_NCCH::ReadUpdateRomFS(std::shared_ptr data; Loader::SMDH smdh; diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 7c86f85d8..041cfddbd 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -59,6 +59,10 @@ public: ResultStatus ReadUpdateRomFS(std::shared_ptr& romfs_file) override; + ResultStatus DumpRomFS(const std::string& target_path) override; + + ResultStatus DumpUpdateRomFS(const std::string& target_path) override; + ResultStatus ReadTitle(std::string& title) override; private: