mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	core: Add dump RomFS support
This is added to LayeredFS, then the NCCH container and then the loader interface.
This commit is contained in:
		
							parent
							
								
									db18f6c79a
								
							
						
					
					
						commit
						13e2d534e9
					
				
					 7 changed files with 131 additions and 7 deletions
				
			
		|  | @ -52,7 +52,7 @@ struct FileMetadata { | |||
| static_assert(sizeof(FileMetadata) == 0x20, "Size of FileMetadata is not correct"); | ||||
| 
 | ||||
| LayeredFS::LayeredFS(std::shared_ptr<RomFSReader> 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<RomFSReader> 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<u8, BufferSize> 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<std::size_t>(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
 | ||||
|  |  | |||
|  | @ -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<RomFSReader> 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<RomFSReader> romfs; | ||||
|  |  | |||
|  | @ -597,7 +597,8 @@ Loader::ResultStatus NCCHContainer::LoadOverrideExeFSSection(const char* name, | |||
|     return Loader::ResultStatus::ErrorNotUsed; | ||||
| } | ||||
| 
 | ||||
| Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& romfs_file) { | ||||
| Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& 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<RomFSReader>& 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<LayeredFS>(std::move(direct_romfs), path + "romfs/", | ||||
|                                                  path + "romfs_ext/"); | ||||
|     } else { | ||||
|  | @ -650,6 +653,21 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& romf | |||
|     return Loader::ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| Loader::ResultStatus NCCHContainer::DumpRomFS(const std::string& target_path) { | ||||
|     std::shared_ptr<RomFSReader> direct_romfs; | ||||
|     Loader::ResultStatus result = ReadRomFS(direct_romfs, false); | ||||
|     if (result != Loader::ResultStatus::Success) | ||||
|         return result; | ||||
| 
 | ||||
|     std::shared_ptr<LayeredFS> layered_fs = | ||||
|         std::make_shared<LayeredFS>(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<RomFSReader>& romfs_file) { | ||||
|     // Check for RomFS overrides
 | ||||
|     std::string split_filepath = filepath + ".romfs"; | ||||
|  |  | |||
|  | @ -247,7 +247,15 @@ public: | |||
|      * @param size The size of the romfs | ||||
|      * @return ResultStatus result of function | ||||
|      */ | ||||
|     Loader::ResultStatus ReadRomFS(std::shared_ptr<RomFSReader>& romfs_file); | ||||
|     Loader::ResultStatus ReadRomFS(std::shared_ptr<RomFSReader>& 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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -254,6 +254,18 @@ ResultStatus AppLoader_NCCH::ReadUpdateRomFS(std::shared_ptr<FileSys::RomFSReade | |||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_NCCH::DumpRomFS(const std::string& target_path) { | ||||
|     return base_ncch.DumpRomFS(target_path); | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_NCCH::DumpUpdateRomFS(const std::string& target_path) { | ||||
|     u64 program_id; | ||||
|     ReadProgramId(program_id); | ||||
|     update_ncch.OpenFile(Service::AM::GetTitleContentPath(Service::FS::MediaType::SDMC, | ||||
|                                                           program_id | UPDATE_MASK)); | ||||
|     return update_ncch.DumpRomFS(target_path); | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) { | ||||
|     std::vector<u8> data; | ||||
|     Loader::SMDH smdh; | ||||
|  |  | |||
|  | @ -59,6 +59,10 @@ public: | |||
| 
 | ||||
|     ResultStatus ReadUpdateRomFS(std::shared_ptr<FileSys::RomFSReader>& 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: | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue