diff options
| author | 2018-07-27 19:10:05 -0700 | |
|---|---|---|
| committer | 2018-07-27 19:10:05 -0700 | |
| commit | abb489418834848965f6ccb38d2cd7cf01cedf35 (patch) | |
| tree | beff5b868534a8bfb4eea7992553fa5fb5db5287 /src/core/file_sys/romfs.cpp | |
| parent | Merge pull request #845 from lioncash/nfc (diff) | |
| parent | RomFS Extraction (diff) | |
| download | yuzu-abb489418834848965f6ccb38d2cd7cf01cedf35.tar.gz yuzu-abb489418834848965f6ccb38d2cd7cf01cedf35.tar.xz yuzu-abb489418834848965f6ccb38d2cd7cf01cedf35.zip | |
Merge pull request #696 from DarkLordZach/romfs
RomFS Extraction
Diffstat (limited to 'src/core/file_sys/romfs.cpp')
| -rw-r--r-- | src/core/file_sys/romfs.cpp | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp new file mode 100644 index 000000000..ff3ddb29c --- /dev/null +++ b/src/core/file_sys/romfs.cpp | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/swap.h" | ||
| 7 | #include "core/file_sys/romfs.h" | ||
| 8 | #include "core/file_sys/vfs.h" | ||
| 9 | #include "core/file_sys/vfs_offset.h" | ||
| 10 | #include "core/file_sys/vfs_vector.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF; | ||
| 15 | |||
| 16 | struct TableLocation { | ||
| 17 | u64_le offset; | ||
| 18 | u64_le size; | ||
| 19 | }; | ||
| 20 | static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size."); | ||
| 21 | |||
| 22 | struct RomFSHeader { | ||
| 23 | u64_le header_size; | ||
| 24 | TableLocation directory_hash; | ||
| 25 | TableLocation directory_meta; | ||
| 26 | TableLocation file_hash; | ||
| 27 | TableLocation file_meta; | ||
| 28 | u64_le data_offset; | ||
| 29 | }; | ||
| 30 | static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); | ||
| 31 | |||
| 32 | struct DirectoryEntry { | ||
| 33 | u32_le sibling; | ||
| 34 | u32_le child_dir; | ||
| 35 | u32_le child_file; | ||
| 36 | u32_le hash; | ||
| 37 | u32_le name_length; | ||
| 38 | }; | ||
| 39 | static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); | ||
| 40 | |||
| 41 | struct FileEntry { | ||
| 42 | u32_le parent; | ||
| 43 | u32_le sibling; | ||
| 44 | u64_le offset; | ||
| 45 | u64_le size; | ||
| 46 | u32_le hash; | ||
| 47 | u32_le name_length; | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); | ||
| 50 | |||
| 51 | template <typename Entry> | ||
| 52 | static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) { | ||
| 53 | Entry entry{}; | ||
| 54 | if (file->ReadObject(&entry, offset) != sizeof(Entry)) | ||
| 55 | return {}; | ||
| 56 | std::string string(entry.name_length, '\0'); | ||
| 57 | if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size()) | ||
| 58 | return {}; | ||
| 59 | return {entry, string}; | ||
| 60 | } | ||
| 61 | |||
| 62 | void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset, | ||
| 63 | std::shared_ptr<VectorVfsDirectory> parent) { | ||
| 64 | while (true) { | ||
| 65 | auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); | ||
| 66 | |||
| 67 | parent->AddFile(std::make_shared<OffsetVfsFile>( | ||
| 68 | file, entry.first.size, entry.first.offset + data_offset, entry.second, parent)); | ||
| 69 | |||
| 70 | if (entry.first.sibling == ROMFS_ENTRY_EMPTY) | ||
| 71 | break; | ||
| 72 | |||
| 73 | this_file_offset = entry.first.sibling; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset, | ||
| 78 | u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) { | ||
| 79 | while (true) { | ||
| 80 | auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); | ||
| 81 | auto current = std::make_shared<VectorVfsDirectory>( | ||
| 82 | std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second); | ||
| 83 | |||
| 84 | if (entry.first.child_file != ROMFS_ENTRY_EMPTY) { | ||
| 85 | ProcessFile(file, file_offset, data_offset, entry.first.child_file, current); | ||
| 86 | } | ||
| 87 | |||
| 88 | if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) { | ||
| 89 | ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir, | ||
| 90 | current); | ||
| 91 | } | ||
| 92 | |||
| 93 | parent->AddDirectory(current); | ||
| 94 | if (entry.first.sibling == ROMFS_ENTRY_EMPTY) | ||
| 95 | break; | ||
| 96 | this_dir_offset = entry.first.sibling; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | VirtualDir ExtractRomFS(VirtualFile file) { | ||
| 101 | RomFSHeader header{}; | ||
| 102 | if (file->ReadObject(&header) != sizeof(RomFSHeader)) | ||
| 103 | return nullptr; | ||
| 104 | |||
| 105 | if (header.header_size != sizeof(RomFSHeader)) | ||
| 106 | return nullptr; | ||
| 107 | |||
| 108 | const u64 file_offset = header.file_meta.offset; | ||
| 109 | const u64 dir_offset = header.directory_meta.offset + 4; | ||
| 110 | |||
| 111 | const auto root = | ||
| 112 | std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, | ||
| 113 | file->GetContainingDirectory(), file->GetName()); | ||
| 114 | |||
| 115 | ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); | ||
| 116 | |||
| 117 | VirtualDir out = std::move(root); | ||
| 118 | |||
| 119 | while (out->GetSubdirectory("") != nullptr) | ||
| 120 | out = out->GetSubdirectory(""); | ||
| 121 | |||
| 122 | return out; | ||
| 123 | } | ||
| 124 | } // namespace FileSys | ||