romfs.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <memory>
  4. #include "common/assert.h"
  5. #include "common/common_types.h"
  6. #include "common/string_util.h"
  7. #include "common/swap.h"
  8. #include "core/file_sys/fsmitm_romfsbuild.h"
  9. #include "core/file_sys/romfs.h"
  10. #include "core/file_sys/vfs.h"
  11. #include "core/file_sys/vfs_cached.h"
  12. #include "core/file_sys/vfs_concat.h"
  13. #include "core/file_sys/vfs_offset.h"
  14. #include "core/file_sys/vfs_vector.h"
  15. namespace FileSys {
  16. namespace {
  17. constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
  18. struct TableLocation {
  19. u64_le offset;
  20. u64_le size;
  21. };
  22. static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size.");
  23. struct RomFSHeader {
  24. u64_le header_size;
  25. TableLocation directory_hash;
  26. TableLocation directory_meta;
  27. TableLocation file_hash;
  28. TableLocation file_meta;
  29. u64_le data_offset;
  30. };
  31. static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
  32. struct DirectoryEntry {
  33. u32_le parent;
  34. u32_le sibling;
  35. u32_le child_dir;
  36. u32_le child_file;
  37. u32_le hash;
  38. u32_le name_length;
  39. };
  40. static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size.");
  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. struct RomFSTraversalContext {
  51. RomFSHeader header;
  52. VirtualFile file;
  53. std::vector<u8> directory_meta;
  54. std::vector<u8> file_meta;
  55. };
  56. template <typename EntryType, auto Member>
  57. std::pair<EntryType, std::string> GetEntry(const RomFSTraversalContext& ctx, size_t offset) {
  58. const size_t entry_end = offset + sizeof(EntryType);
  59. const std::vector<u8>& vec = ctx.*Member;
  60. const size_t size = vec.size();
  61. const u8* data = vec.data();
  62. EntryType entry{};
  63. if (entry_end > size) {
  64. return {};
  65. }
  66. std::memcpy(&entry, data + offset, sizeof(EntryType));
  67. const size_t name_length = std::min(entry_end + entry.name_length, size) - entry_end;
  68. std::string name(reinterpret_cast<const char*>(data + entry_end), name_length);
  69. return {entry, std::move(name)};
  70. }
  71. std::pair<DirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx,
  72. size_t directory_offset) {
  73. return GetEntry<DirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset);
  74. }
  75. std::pair<FileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx,
  76. size_t file_offset) {
  77. return GetEntry<FileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset);
  78. }
  79. void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset,
  80. std::shared_ptr<VectorVfsDirectory>& parent) {
  81. while (this_file_offset != ROMFS_ENTRY_EMPTY) {
  82. auto entry = GetFileEntry(ctx, this_file_offset);
  83. parent->AddFile(std::make_shared<OffsetVfsFile>(ctx.file, entry.first.size,
  84. entry.first.offset + ctx.header.data_offset,
  85. std::move(entry.second)));
  86. this_file_offset = entry.first.sibling;
  87. }
  88. }
  89. void ProcessDirectory(const RomFSTraversalContext& ctx, u32 this_dir_offset,
  90. std::shared_ptr<VectorVfsDirectory>& parent) {
  91. while (this_dir_offset != ROMFS_ENTRY_EMPTY) {
  92. auto entry = GetDirectoryEntry(ctx, this_dir_offset);
  93. auto current = std::make_shared<VectorVfsDirectory>(
  94. std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
  95. if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
  96. ProcessFile(ctx, entry.first.child_file, current);
  97. }
  98. if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
  99. ProcessDirectory(ctx, entry.first.child_dir, current);
  100. }
  101. parent->AddDirectory(current);
  102. this_dir_offset = entry.first.sibling;
  103. }
  104. }
  105. } // Anonymous namespace
  106. VirtualDir ExtractRomFS(VirtualFile file) {
  107. auto root_container = std::make_shared<VectorVfsDirectory>();
  108. if (!file) {
  109. return root_container;
  110. }
  111. RomFSTraversalContext ctx{};
  112. if (file->ReadObject(&ctx.header) != sizeof(RomFSHeader)) {
  113. return nullptr;
  114. }
  115. if (ctx.header.header_size != sizeof(RomFSHeader)) {
  116. return nullptr;
  117. }
  118. ctx.file = file;
  119. ctx.directory_meta =
  120. file->ReadBytes(ctx.header.directory_meta.size, ctx.header.directory_meta.offset);
  121. ctx.file_meta = file->ReadBytes(ctx.header.file_meta.size, ctx.header.file_meta.offset);
  122. ProcessDirectory(ctx, 0, root_container);
  123. if (auto root = root_container->GetSubdirectory(""); root) {
  124. return root;
  125. }
  126. ASSERT(false);
  127. return nullptr;
  128. }
  129. VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
  130. if (dir == nullptr)
  131. return nullptr;
  132. RomFSBuildContext ctx{dir, ext};
  133. return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
  134. }
  135. } // namespace FileSys