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