romfs.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <memory>
  4. #include "common/common_types.h"
  5. #include "common/string_util.h"
  6. #include "common/swap.h"
  7. #include "core/file_sys/fsmitm_romfsbuild.h"
  8. #include "core/file_sys/romfs.h"
  9. #include "core/file_sys/vfs.h"
  10. #include "core/file_sys/vfs_concat.h"
  11. #include "core/file_sys/vfs_offset.h"
  12. #include "core/file_sys/vfs_vector.h"
  13. namespace FileSys {
  14. namespace {
  15. constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
  16. struct TableLocation {
  17. u64_le offset;
  18. u64_le size;
  19. };
  20. static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size.");
  21. struct RomFSHeader {
  22. u64_le header_size;
  23. TableLocation directory_hash;
  24. TableLocation directory_meta;
  25. TableLocation file_hash;
  26. TableLocation file_meta;
  27. u64_le data_offset;
  28. };
  29. static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
  30. struct DirectoryEntry {
  31. u32_le sibling;
  32. u32_le child_dir;
  33. u32_le child_file;
  34. u32_le hash;
  35. u32_le name_length;
  36. };
  37. static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
  38. struct FileEntry {
  39. u32_le parent;
  40. u32_le sibling;
  41. u64_le offset;
  42. u64_le size;
  43. u32_le hash;
  44. u32_le name_length;
  45. };
  46. static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
  47. template <typename Entry>
  48. std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
  49. Entry entry{};
  50. if (file->ReadObject(&entry, offset) != sizeof(Entry))
  51. return {};
  52. std::string string(entry.name_length, '\0');
  53. if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size())
  54. return {};
  55. return {entry, string};
  56. }
  57. void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
  58. u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
  59. while (true) {
  60. auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
  61. parent->AddFile(std::make_shared<OffsetVfsFile>(
  62. file, entry.first.size, entry.first.offset + data_offset, entry.second));
  63. if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
  64. break;
  65. this_file_offset = entry.first.sibling;
  66. }
  67. }
  68. void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset,
  69. std::size_t data_offset, u32 this_dir_offset,
  70. std::shared_ptr<VectorVfsDirectory> parent) {
  71. while (true) {
  72. auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
  73. auto current = std::make_shared<VectorVfsDirectory>(
  74. std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
  75. if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
  76. ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
  77. }
  78. if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
  79. ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir,
  80. current);
  81. }
  82. parent->AddDirectory(current);
  83. if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
  84. break;
  85. this_dir_offset = entry.first.sibling;
  86. }
  87. }
  88. } // Anonymous namespace
  89. VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
  90. RomFSHeader header{};
  91. if (file->ReadObject(&header) != sizeof(RomFSHeader))
  92. return nullptr;
  93. if (header.header_size != sizeof(RomFSHeader))
  94. return nullptr;
  95. const u64 file_offset = header.file_meta.offset;
  96. const u64 dir_offset = header.directory_meta.offset + 4;
  97. auto root =
  98. std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
  99. file->GetName(), file->GetContainingDirectory());
  100. ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
  101. VirtualDir out = std::move(root);
  102. if (type == RomFSExtractionType::SingleDiscard)
  103. return out->GetSubdirectories().front();
  104. while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
  105. if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&
  106. type == RomFSExtractionType::Truncated)
  107. break;
  108. out = out->GetSubdirectories().front();
  109. }
  110. return out;
  111. }
  112. VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
  113. if (dir == nullptr)
  114. return nullptr;
  115. RomFSBuildContext ctx{dir, ext};
  116. return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
  117. }
  118. } // namespace FileSys