romfs.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. // Copyright 2017 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <cstring>
  5. #include "common/swap.h"
  6. #include "core/hle/romfs.h"
  7. namespace RomFS {
  8. struct Header {
  9. u32_le header_length;
  10. u32_le dir_hash_table_offset;
  11. u32_le dir_hash_table_length;
  12. u32_le dir_table_offset;
  13. u32_le dir_table_length;
  14. u32_le file_hash_table_offset;
  15. u32_le file_hash_table_length;
  16. u32_le file_table_offset;
  17. u32_le file_table_length;
  18. u32_le data_offset;
  19. };
  20. static_assert(sizeof(Header) == 0x28, "Header has incorrect size");
  21. struct DirectoryMetadata {
  22. u32_le parent_dir_offset;
  23. u32_le next_dir_offset;
  24. u32_le first_child_dir_offset;
  25. u32_le first_file_offset;
  26. u32_le same_hash_next_dir_offset;
  27. u32_le name_length; // in bytes
  28. // followed by directory name
  29. };
  30. static_assert(sizeof(DirectoryMetadata) == 0x18, "DirectoryMetadata has incorrect size");
  31. struct FileMetadata {
  32. u32_le parent_dir_offset;
  33. u32_le next_file_offset;
  34. u64_le data_offset;
  35. u64_le data_length;
  36. u32_le same_hash_next_file_offset;
  37. u32_le name_length; // in bytes
  38. // followed by file name
  39. };
  40. static_assert(sizeof(FileMetadata) == 0x20, "FileMetadata has incorrect size");
  41. static bool MatchName(const u8* buffer, u32 name_length, const std::u16string& name) {
  42. std::vector<char16_t> name_buffer(name_length / sizeof(char16_t));
  43. std::memcpy(name_buffer.data(), buffer, name_length);
  44. return name == std::u16string(name_buffer.begin(), name_buffer.end());
  45. }
  46. const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path) {
  47. constexpr u32 INVALID_FIELD = 0xFFFFFFFF;
  48. // Split path into directory names and file name
  49. std::vector<std::u16string> dir_names = path;
  50. dir_names.pop_back();
  51. const std::u16string& file_name = path.back();
  52. Header header;
  53. std::memcpy(&header, romfs, sizeof(header));
  54. // Find directories of each level
  55. DirectoryMetadata dir;
  56. const u8* current_dir = romfs + header.dir_table_offset;
  57. std::memcpy(&dir, current_dir, sizeof(dir));
  58. for (const std::u16string& dir_name : dir_names) {
  59. u32 child_dir_offset;
  60. child_dir_offset = dir.first_child_dir_offset;
  61. while (true) {
  62. if (child_dir_offset == INVALID_FIELD) {
  63. return nullptr;
  64. }
  65. const u8* current_child_dir = romfs + header.dir_table_offset + child_dir_offset;
  66. std::memcpy(&dir, current_child_dir, sizeof(dir));
  67. if (MatchName(current_child_dir + sizeof(dir), dir.name_length, dir_name)) {
  68. current_dir = current_child_dir;
  69. break;
  70. }
  71. child_dir_offset = dir.next_dir_offset;
  72. }
  73. }
  74. // Find the file
  75. FileMetadata file;
  76. u32 file_offset = dir.first_file_offset;
  77. while (file_offset != INVALID_FIELD) {
  78. const u8* current_file = romfs + header.file_table_offset + file_offset;
  79. std::memcpy(&file, current_file, sizeof(file));
  80. if (MatchName(current_file + sizeof(file), file.name_length, file_name)) {
  81. return romfs + header.data_offset + file.data_offset;
  82. }
  83. file_offset = file.next_file_offset;
  84. }
  85. return nullptr;
  86. }
  87. } // namespace RomFS