summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/file_sys/bis_factory.cpp2
-rw-r--r--src/core/file_sys/bis_factory.h2
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp358
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h70
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.hpp106
-rw-r--r--src/core/file_sys/patch_manager.cpp66
-rw-r--r--src/core/file_sys/registered_cache.cpp9
-rw-r--r--src/core/file_sys/romfs.cpp9
-rw-r--r--src/core/file_sys/romfs.h8
-rw-r--r--src/core/file_sys/vfs.cpp4
-rw-r--r--src/core/file_sys/vfs.h5
-rw-r--r--src/core/file_sys/vfs_concat.cpp21
-rw-r--r--src/core/file_sys/vfs_concat.h58
-rw-r--r--src/core/file_sys/vfs_layered.cpp15
-rw-r--r--src/core/file_sys/vfs_layered.h2
-rw-r--r--src/core/file_sys/vfs_real.cpp4
-rw-r--r--src/core/file_sys/vfs_real.h2
-rw-r--r--src/core/file_sys/vfs_static.h3
-rw-r--r--src/core/file_sys/vfs_vector.cpp7
-rw-r--r--src/core/file_sys/vfs_vector.h1
-rw-r--r--src/yuzu/main.cpp2
22 files changed, 378 insertions, 378 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ead86fd85..23fd6e920 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,7 +33,7 @@ add_library(core STATIC
33 file_sys/directory.h 33 file_sys/directory.h
34 file_sys/errors.h 34 file_sys/errors.h
35 file_sys/fsmitm_romfsbuild.cpp 35 file_sys/fsmitm_romfsbuild.cpp
36 file_sys/fsmitm_romfsbuild.hpp 36 file_sys/fsmitm_romfsbuild.h
37 file_sys/mode.h 37 file_sys/mode.h
38 file_sys/nca_metadata.cpp 38 file_sys/nca_metadata.cpp
39 file_sys/nca_metadata.h 39 file_sys/nca_metadata.h
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 012e08e7d..6102ef476 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -2,9 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <fmt/format.h>
5#include "core/file_sys/bis_factory.h" 6#include "core/file_sys/bis_factory.h"
6#include "core/file_sys/registered_cache.h" 7#include "core/file_sys/registered_cache.h"
7#include "fmt/format.h"
8 8
9namespace FileSys { 9namespace FileSys {
10 10
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 0d81967cc..c352e0925 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -17,7 +17,7 @@ class RegisteredCache;
17/// registered caches. 17/// registered caches.
18class BISFactory { 18class BISFactory {
19public: 19public:
20 BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index a6fe6719d..21fc3d796 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -24,9 +24,9 @@
24 24
25#include <cstring> 25#include <cstring>
26#include "common/assert.h" 26#include "common/assert.h"
27#include "fsmitm_romfsbuild.hpp" 27#include "core/file_sys/fsmitm_romfsbuild.h"
28#include "vfs.h" 28#include "core/file_sys/vfs.h"
29#include "vfs_vector.h" 29#include "core/file_sys/vfs_vector.h"
30 30
31namespace FileSys { 31namespace FileSys {
32 32
@@ -35,7 +35,7 @@ constexpr u64 FS_MAX_PATH = 0x301;
35constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF; 35constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
36constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200; 36constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
37 37
38/* Types for building a RomFS. */ 38// Types for building a RomFS.
39struct RomFSHeader { 39struct RomFSHeader {
40 u64 header_size; 40 u64 header_size;
41 u64 dir_hash_table_ofs; 41 u64 dir_hash_table_ofs;
@@ -57,7 +57,6 @@ struct RomFSDirectoryEntry {
57 u32 file; 57 u32 file;
58 u32 hash; 58 u32 hash;
59 u32 name_size; 59 u32 name_size;
60 char name[];
61}; 60};
62static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size."); 61static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
63 62
@@ -68,37 +67,63 @@ struct RomFSFileEntry {
68 u64 size; 67 u64 size;
69 u32 hash; 68 u32 hash;
70 u32 name_size; 69 u32 name_size;
71 char name[];
72}; 70};
73static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size."); 71static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
74 72
75struct RomFSBuildFileContext; 73struct RomFSBuildFileContext;
76 74
77struct RomFSBuildDirectoryContext { 75struct RomFSBuildDirectoryContext {
78 char* path; 76 std::string path = "";
79 u32 cur_path_ofs; 77 u32 cur_path_ofs = 0;
80 u32 path_len; 78 u32 path_len = 0;
81 u32 entry_offset = 0; 79 u32 entry_offset = 0;
82 RomFSBuildDirectoryContext* parent = nullptr; 80 std::shared_ptr<RomFSBuildDirectoryContext> parent;
83 RomFSBuildDirectoryContext* child = nullptr; 81 std::shared_ptr<RomFSBuildDirectoryContext> child;
84 RomFSBuildDirectoryContext* sibling = nullptr; 82 std::shared_ptr<RomFSBuildDirectoryContext> sibling;
85 RomFSBuildFileContext* file = nullptr; 83 std::shared_ptr<RomFSBuildFileContext> file;
86}; 84};
87 85
88struct RomFSBuildFileContext { 86struct RomFSBuildFileContext {
89 char* path; 87 std::string path = "";
90 u32 cur_path_ofs; 88 u32 cur_path_ofs = 0;
91 u32 path_len; 89 u32 path_len = 0;
92 u32 entry_offset = 0; 90 u32 entry_offset = 0;
93 u64 offset = 0; 91 u64 offset = 0;
94 u64 size = 0; 92 u64 size = 0;
95 RomFSBuildDirectoryContext* parent = nullptr; 93 std::shared_ptr<RomFSBuildDirectoryContext> parent;
96 RomFSBuildFileContext* sibling = nullptr; 94 std::shared_ptr<RomFSBuildFileContext> sibling;
97 VirtualFile source = nullptr; 95 VirtualFile source = nullptr;
96
97 RomFSBuildFileContext() : path(""), cur_path_ofs(0), path_len(0) {}
98}; 98};
99 99
100void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, RomFSBuildDirectoryContext* parent) { 100static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t path_len) {
101 std::vector<RomFSBuildDirectoryContext*> child_dirs; 101 u32 hash = parent ^ 123456789;
102 for (u32 i = 0; i < path_len; i++) {
103 hash = (hash >> 5) | (hash << 27);
104 hash ^= path[start + i];
105 }
106
107 return hash;
108}
109
110static u32 romfs_get_hash_table_count(u32 num_entries) {
111 if (num_entries < 3) {
112 return 3;
113 } else if (num_entries < 19) {
114 return num_entries | 1;
115 }
116 u32 count = num_entries;
117 while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
118 count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
119 count++;
120 }
121 return count;
122}
123
124void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
125 std::shared_ptr<RomFSBuildDirectoryContext> parent) {
126 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
102 127
103 VirtualDir dir; 128 VirtualDir dir;
104 129
@@ -111,41 +136,33 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, RomFSBuildDirector
111 136
112 for (const auto& kv : entries) { 137 for (const auto& kv : entries) {
113 if (kv.second == VfsEntryType::Directory) { 138 if (kv.second == VfsEntryType::Directory) {
114 RomFSBuildDirectoryContext* child = new RomFSBuildDirectoryContext({0}); 139 const auto child = std::make_shared<RomFSBuildDirectoryContext>();
115 /* Set child's path. */ 140 // Set child's path.
116 child->cur_path_ofs = parent->path_len + 1; 141 child->cur_path_ofs = parent->path_len + 1;
117 child->path_len = child->cur_path_ofs + kv.first.size(); 142 child->path_len = child->cur_path_ofs + kv.first.size();
118 child->path = new char[child->path_len + 1]; 143 child->path = parent->path + "/" + kv.first;
119 strcpy(child->path, parent->path); 144
145 // Sanity check on path_len
120 ASSERT(child->path_len < FS_MAX_PATH); 146 ASSERT(child->path_len < FS_MAX_PATH);
121 strcat(child->path + parent->path_len, "/");
122 strcat(child->path + parent->path_len, kv.first.c_str());
123 147
124 if (!this->AddDirectory(parent, child, nullptr)) { 148 if (AddDirectory(parent, child)) {
125 delete child->path;
126 delete child;
127 } else {
128 child_dirs.push_back(child); 149 child_dirs.push_back(child);
129 } 150 }
130 } else { 151 } else {
131 RomFSBuildFileContext* child = new RomFSBuildFileContext({0}); 152 const auto child = std::make_shared<RomFSBuildFileContext>();
132 /* Set child's path. */ 153 // Set child's path.
133 child->cur_path_ofs = parent->path_len + 1; 154 child->cur_path_ofs = parent->path_len + 1;
134 child->path_len = child->cur_path_ofs + kv.first.size(); 155 child->path_len = child->cur_path_ofs + kv.first.size();
135 child->path = new char[child->path_len + 1]; 156 child->path = parent->path + "/" + kv.first;
136 strcpy(child->path, parent->path); 157
158 // Sanity check on path_len
137 ASSERT(child->path_len < FS_MAX_PATH); 159 ASSERT(child->path_len < FS_MAX_PATH);
138 strcat(child->path + parent->path_len, "/");
139 strcat(child->path + parent->path_len, kv.first.c_str());
140 160
141 child->source = root_romfs->GetFileRelative(child->path); 161 child->source = root_romfs->GetFileRelative(child->path);
142 162
143 child->size = child->source->GetSize(); 163 child->size = child->source->GetSize();
144 164
145 if (!this->AddFile(parent, child)) { 165 AddFile(parent, child);
146 delete child->path;
147 delete child;
148 }
149 } 166 }
150 } 167 }
151 168
@@ -154,213 +171,200 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, RomFSBuildDirector
154 } 171 }
155} 172}
156 173
157bool RomFSBuildContext::AddDirectory(RomFSBuildDirectoryContext* parent_dir_ctx, 174bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
158 RomFSBuildDirectoryContext* dir_ctx, 175 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
159 RomFSBuildDirectoryContext** out_dir_ctx) { 176 // Check whether it's already in the known directories.
160 /* Check whether it's already in the known directories. */ 177 const auto existing = directories.find(dir_ctx->path);
161 auto existing = this->directories.find(dir_ctx->path); 178 if (existing != directories.end())
162 if (existing != this->directories.end()) {
163 if (out_dir_ctx) {
164 *out_dir_ctx = existing->second;
165 }
166 return false; 179 return false;
167 }
168 180
169 /* Add a new directory. */ 181 // Add a new directory.
170 this->num_dirs++; 182 num_dirs++;
171 this->dir_table_size += 183 dir_table_size +=
172 sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3); 184 sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3);
173 dir_ctx->parent = parent_dir_ctx; 185 dir_ctx->parent = parent_dir_ctx;
174 this->directories.insert({dir_ctx->path, dir_ctx}); 186 directories.emplace(dir_ctx->path, dir_ctx);
175 187
176 if (out_dir_ctx) {
177 *out_dir_ctx = dir_ctx;
178 }
179 return true; 188 return true;
180} 189}
181 190
182bool RomFSBuildContext::AddFile(RomFSBuildDirectoryContext* parent_dir_ctx, 191bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
183 RomFSBuildFileContext* file_ctx) { 192 std::shared_ptr<RomFSBuildFileContext> file_ctx) {
184 /* Check whether it's already in the known files. */ 193 // Check whether it's already in the known files.
185 auto existing = this->files.find(file_ctx->path); 194 const auto existing = files.find(file_ctx->path);
186 if (existing != this->files.end()) { 195 if (existing != files.end()) {
187 return false; 196 return false;
188 } 197 }
189 198
190 /* Add a new file. */ 199 // Add a new file.
191 this->num_files++; 200 num_files++;
192 this->file_table_size += 201 file_table_size +=
193 sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3); 202 sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3);
194 file_ctx->parent = parent_dir_ctx; 203 file_ctx->parent = parent_dir_ctx;
195 this->files.insert({file_ctx->path, file_ctx}); 204 files.emplace(file_ctx->path, file_ctx);
196 205
197 return true; 206 return true;
198} 207}
199 208
200RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) { 209RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) {
201 this->root = new RomFSBuildDirectoryContext({0}); 210 root = std::make_shared<RomFSBuildDirectoryContext>();
202 this->root->path = new char[1]; 211 root->path = "\0";
203 this->root->path[0] = '\x00'; 212 directories.emplace(root->path, root);
204 this->directories.insert({this->root->path, this->root}); 213 num_dirs = 1;
205 this->num_dirs = 1; 214 dir_table_size = 0x18;
206 this->dir_table_size = 0x18; 215
207 216 VisitDirectory(base, root);
208 VisitDirectory(base, this->root);
209} 217}
210 218
219RomFSBuildContext::~RomFSBuildContext() = default;
220
211std::map<u64, VirtualFile> RomFSBuildContext::Build() { 221std::map<u64, VirtualFile> RomFSBuildContext::Build() {
212 std::map<u64, VirtualFile> out; 222 const auto dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
213 RomFSBuildFileContext* cur_file; 223 const auto file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
214 RomFSBuildDirectoryContext* cur_dir; 224 dir_hash_table_size = 4 * dir_hash_table_entry_count;
215 225 file_hash_table_size = 4 * file_hash_table_entry_count;
216 const auto dir_hash_table_entry_count = romfs_get_hash_table_count(this->num_dirs); 226
217 const auto file_hash_table_entry_count = romfs_get_hash_table_count(this->num_files); 227 // Assign metadata pointers
218 this->dir_hash_table_size = 4 * dir_hash_table_entry_count; 228 RomFSHeader header{};
219 this->file_hash_table_size = 4 * file_hash_table_entry_count; 229
220 230 std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
221 /* Assign metadata pointers */ 231 std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
222 RomFSHeader* header = new RomFSHeader({0}); 232
223 auto metadata = new u8[this->dir_hash_table_size + this->dir_table_size + 233 std::vector<u8> dir_table(dir_table_size);
224 this->file_hash_table_size + this->file_table_size]; 234 std::vector<u8> file_table(file_table_size);
225 auto dir_hash_table = reinterpret_cast<u32*>(metadata); 235
226 const auto dir_table = reinterpret_cast<RomFSDirectoryEntry*>( 236 // Clear out hash tables.
227 reinterpret_cast<uintptr_t>(dir_hash_table) + this->dir_hash_table_size);
228 auto file_hash_table =
229 reinterpret_cast<u32*>(reinterpret_cast<uintptr_t>(dir_table) + this->dir_table_size);
230 const auto file_table = reinterpret_cast<RomFSFileEntry*>(
231 reinterpret_cast<uintptr_t>(file_hash_table) + this->file_hash_table_size);
232
233 /* Clear out hash tables. */
234 for (u32 i = 0; i < dir_hash_table_entry_count; i++) 237 for (u32 i = 0; i < dir_hash_table_entry_count; i++)
235 dir_hash_table[i] = ROMFS_ENTRY_EMPTY; 238 dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
236 for (u32 i = 0; i < file_hash_table_entry_count; i++) 239 for (u32 i = 0; i < file_hash_table_entry_count; i++)
237 file_hash_table[i] = ROMFS_ENTRY_EMPTY; 240 file_hash_table[i] = ROMFS_ENTRY_EMPTY;
238 241
239 out.clear(); 242 std::shared_ptr<RomFSBuildFileContext> cur_file;
240 243
241 /* Determine file offsets. */ 244 // Determine file offsets.
242 u32 entry_offset = 0; 245 u32 entry_offset = 0;
243 RomFSBuildFileContext* prev_file = nullptr; 246 std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
244 for (const auto& it : this->files) { 247 for (const auto& it : files) {
245 cur_file = it.second; 248 cur_file = it.second;
246 this->file_partition_size = (this->file_partition_size + 0xFULL) & ~0xFULL; 249 file_partition_size = (file_partition_size + 0xFULL) & ~0xFULL;
247 cur_file->offset = this->file_partition_size; 250 cur_file->offset = file_partition_size;
248 this->file_partition_size += cur_file->size; 251 file_partition_size += cur_file->size;
249 cur_file->entry_offset = entry_offset; 252 cur_file->entry_offset = entry_offset;
250 entry_offset += 253 entry_offset +=
251 sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3); 254 sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3);
252 prev_file = cur_file; 255 prev_file = cur_file;
253 } 256 }
254 /* Assign deferred parent/sibling ownership. */ 257 // Assign deferred parent/sibling ownership.
255 for (auto it = this->files.rbegin(); it != this->files.rend(); it++) { 258 for (auto it = files.rbegin(); it != files.rend(); ++it) {
256 cur_file = it->second; 259 cur_file = it->second;
257 cur_file->sibling = cur_file->parent->file; 260 cur_file->sibling = cur_file->parent->file;
258 cur_file->parent->file = cur_file; 261 cur_file->parent->file = cur_file;
259 } 262 }
260 263
261 /* Determine directory offsets. */ 264 std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
265
266 // Determine directory offsets.
262 entry_offset = 0; 267 entry_offset = 0;
263 for (const auto& it : this->directories) { 268 for (const auto& it : directories) {
264 cur_dir = it.second; 269 cur_dir = it.second;
265 cur_dir->entry_offset = entry_offset; 270 cur_dir->entry_offset = entry_offset;
266 entry_offset += 271 entry_offset +=
267 sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3); 272 sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3);
268 } 273 }
269 /* Assign deferred parent/sibling ownership. */ 274 // Assign deferred parent/sibling ownership.
270 for (auto it = this->directories.rbegin(); it->second != this->root; it++) { 275 for (auto it = directories.rbegin(); it->second != root; ++it) {
271 cur_dir = it->second; 276 cur_dir = it->second;
272 cur_dir->sibling = cur_dir->parent->child; 277 cur_dir->sibling = cur_dir->parent->child;
273 cur_dir->parent->child = cur_dir; 278 cur_dir->parent->child = cur_dir;
274 } 279 }
275 280
276 /* Populate file tables. */ 281 std::map<u64, VirtualFile> out;
277 for (const auto& it : this->files) { 282
283 // Populate file tables.
284 for (const auto& it : files) {
278 cur_file = it.second; 285 cur_file = it.second;
279 RomFSFileEntry* cur_entry = romfs_get_fentry(file_table, cur_file->entry_offset); 286 RomFSFileEntry cur_entry{};
280 287
281 cur_entry->parent = cur_file->parent->entry_offset; 288 cur_entry.parent = cur_file->parent->entry_offset;
282 cur_entry->sibling = 289 cur_entry.sibling =
283 (cur_file->sibling == nullptr) ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset; 290 cur_file->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
284 cur_entry->offset = cur_file->offset; 291 cur_entry.offset = cur_file->offset;
285 cur_entry->size = cur_file->size; 292 cur_entry.size = cur_file->size;
286 293
287 const auto name_size = cur_file->path_len - cur_file->cur_path_ofs; 294 const auto name_size = cur_file->path_len - cur_file->cur_path_ofs;
288 const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, 295 const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, cur_file->path,
289 reinterpret_cast<unsigned char*>(cur_file->path) + 296 cur_file->cur_path_ofs, name_size);
290 cur_file->cur_path_ofs, 297 cur_entry.hash = file_hash_table[hash % file_hash_table_entry_count];
291 0, name_size);
292 cur_entry->hash = file_hash_table[hash % file_hash_table_entry_count];
293 file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset; 298 file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
294 299
295 cur_entry->name_size = name_size; 300 cur_entry.name_size = name_size;
296 memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
297 memcpy(cur_entry->name, cur_file->path + cur_file->cur_path_ofs, name_size);
298 301
299 out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source); 302 out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
303 std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
304 std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
305 (cur_entry.name_size + 3) & ~3);
306 std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry),
307 cur_file->path.data() + cur_file->cur_path_ofs, name_size);
300 } 308 }
301 309
302 /* Populate dir tables. */ 310 // Populate dir tables.
303 for (const auto& it : this->directories) { 311 for (const auto& it : directories) {
304 cur_dir = it.second; 312 cur_dir = it.second;
305 RomFSDirectoryEntry* cur_entry = romfs_get_direntry(dir_table, cur_dir->entry_offset); 313 RomFSDirectoryEntry cur_entry{};
306 cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset; 314
307 cur_entry->sibling = 315 cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
308 (cur_dir->sibling == nullptr) ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset; 316 cur_entry.sibling =
309 cur_entry->child = 317 cur_dir->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
310 (cur_dir->child == nullptr) ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset; 318 cur_entry.child =
311 cur_entry->file = 319 cur_dir->child == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
312 (cur_dir->file == nullptr) ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset; 320 cur_entry.file = cur_dir->file == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
313 321
314 u32 name_size = cur_dir->path_len - cur_dir->cur_path_ofs; 322 const auto name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
315 u32 hash = romfs_calc_path_hash( 323 const auto hash = romfs_calc_path_hash(cur_dir == root ? 0 : cur_dir->parent->entry_offset,
316 cur_dir == this->root ? 0 : cur_dir->parent->entry_offset, 324 cur_dir->path, cur_dir->cur_path_ofs, name_size);
317 reinterpret_cast<unsigned char*>(cur_dir->path) + cur_dir->cur_path_ofs, 0, name_size); 325 cur_entry.hash = dir_hash_table[hash % dir_hash_table_entry_count];
318 cur_entry->hash = dir_hash_table[hash % dir_hash_table_entry_count];
319 dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset; 326 dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
320 327
321 cur_entry->name_size = name_size; 328 cur_entry.name_size = name_size;
322 memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
323 memcpy(cur_entry->name, cur_dir->path + cur_dir->cur_path_ofs, name_size);
324 }
325 329
326 /* Delete directories. */ 330 std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
327 for (const auto& it : this->directories) { 331 sizeof(RomFSDirectoryEntry));
328 cur_dir = it.second; 332 std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
329 delete cur_dir->path; 333 sizeof(RomFSDirectoryEntry));
330 delete cur_dir; 334 std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
335 (cur_entry.name_size + 3) & ~3);
336 std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry),
337 cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
331 } 338 }
332 this->root = nullptr;
333 this->directories.clear();
334 339
335 /* Delete files. */ 340 // Set header fields.
336 for (const auto& it : this->files) { 341 header.header_size = sizeof(RomFSHeader);
337 cur_file = it.second; 342 header.file_hash_table_size = file_hash_table_size;
338 delete cur_file->path; 343 header.file_table_size = file_table_size;
339 delete cur_file; 344 header.dir_hash_table_size = dir_hash_table_size;
340 } 345 header.dir_table_size = dir_table_size;
341 this->files.clear(); 346 header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
342 347 header.dir_hash_table_ofs = (header.file_partition_ofs + file_partition_size + 3ULL) & ~3ULL;
343 /* Set header fields. */ 348 header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
344 header->header_size = sizeof(*header); 349 header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
345 header->file_hash_table_size = this->file_hash_table_size; 350 header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
346 header->file_table_size = this->file_table_size;
347 header->dir_hash_table_size = this->dir_hash_table_size;
348 header->dir_table_size = this->dir_table_size;
349 header->file_partition_ofs = ROMFS_FILEPARTITION_OFS;
350 header->dir_hash_table_ofs =
351 (header->file_partition_ofs + this->file_partition_size + 3ULL) & ~3ULL;
352 header->dir_table_ofs = header->dir_hash_table_ofs + header->dir_hash_table_size;
353 header->file_hash_table_ofs = header->dir_table_ofs + header->dir_table_size;
354 header->file_table_ofs = header->file_hash_table_ofs + header->file_hash_table_size;
355 351
356 std::vector<u8> header_data(sizeof(RomFSHeader)); 352 std::vector<u8> header_data(sizeof(RomFSHeader));
357 std::memcpy(header_data.data(), header, header_data.size()); 353 std::memcpy(header_data.data(), &header, header_data.size());
358 out.emplace(0, std::make_shared<VectorVfsFile>(header_data)); 354 out.emplace(0, std::make_shared<VectorVfsFile>(header_data));
359 355
360 std::vector<u8> meta_out(this->file_hash_table_size + this->file_table_size + 356 std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
361 this->dir_hash_table_size + this->dir_table_size); 357 dir_table_size);
362 std::memcpy(meta_out.data(), metadata, meta_out.size()); 358 auto index = 0;
363 out.emplace(header->dir_hash_table_ofs, std::make_shared<VectorVfsFile>(meta_out)); 359 std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
360 index += dir_hash_table.size() * sizeof(u32);
361 std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
362 index += dir_table.size();
363 std::memcpy(metadata.data() + index, file_hash_table.data(),
364 file_hash_table.size() * sizeof(u32));
365 index += file_hash_table.size() * sizeof(u32);
366 std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
367 out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(metadata));
364 368
365 return out; 369 return out;
366} 370}
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
new file mode 100644
index 000000000..b0c3c123b
--- /dev/null
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -0,0 +1,70 @@
1/*
2 * Copyright (c) 2018 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17/*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2018 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25#pragma once
26
27#include <map>
28#include <memory>
29#include <string>
30#include <boost/detail/container_fwd.hpp>
31#include "common/common_types.h"
32#include "core/file_sys/vfs.h"
33
34namespace FileSys {
35
36struct RomFSBuildDirectoryContext;
37struct RomFSBuildFileContext;
38struct RomFSDirectoryEntry;
39struct RomFSFileEntry;
40
41class RomFSBuildContext {
42public:
43 explicit RomFSBuildContext(VirtualDir base);
44 ~RomFSBuildContext();
45
46 // This finalizes the context.
47 std::map<u64, VirtualFile> Build();
48
49private:
50 VirtualDir base;
51 std::shared_ptr<RomFSBuildDirectoryContext> root;
52 std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
53 std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
54 u64 num_dirs = 0;
55 u64 num_files = 0;
56 u64 dir_table_size = 0;
57 u64 file_table_size = 0;
58 u64 dir_hash_table_size = 0;
59 u64 file_hash_table_size = 0;
60 u64 file_partition_size = 0;
61
62 void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent);
63
64 bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
65 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
66 bool AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
67 std::shared_ptr<RomFSBuildFileContext> file_ctx);
68};
69
70} // namespace FileSys
diff --git a/src/core/file_sys/fsmitm_romfsbuild.hpp b/src/core/file_sys/fsmitm_romfsbuild.hpp
deleted file mode 100644
index b897aab21..000000000
--- a/src/core/file_sys/fsmitm_romfsbuild.hpp
+++ /dev/null
@@ -1,106 +0,0 @@
1/*
2 * Copyright (c) 2018 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17/*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2018 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25#pragma once
26#include <map>
27#include <boost/detail/container_fwd.hpp>
28#include "common/common_types.h"
29#include "vfs.h"
30
31namespace FileSys {
32
33/* Used as comparator for std::map<char *, RomFSBuild*Context> */
34struct build_ctx_cmp {
35 bool operator()(const char* a, const char* b) const {
36 return strcmp(a, b) < 0;
37 }
38};
39
40struct RomFSDirectoryEntry;
41struct RomFSFileEntry;
42struct RomFSBuildDirectoryContext;
43struct RomFSBuildFileContext;
44
45class RomFSBuildContext {
46private:
47 VirtualDir base;
48 RomFSBuildDirectoryContext* root;
49 std::map<char*, RomFSBuildDirectoryContext*, build_ctx_cmp> directories;
50 std::map<char*, RomFSBuildFileContext*, build_ctx_cmp> files;
51 u64 num_dirs = 0;
52 u64 num_files = 0;
53 u64 dir_table_size = 0;
54 u64 file_table_size = 0;
55 u64 dir_hash_table_size = 0;
56 u64 file_hash_table_size = 0;
57 u64 file_partition_size = 0;
58
59 void VisitDirectory(VirtualDir filesys, RomFSBuildDirectoryContext* parent);
60
61 bool AddDirectory(RomFSBuildDirectoryContext* parent_dir_ctx,
62 RomFSBuildDirectoryContext* dir_ctx,
63 RomFSBuildDirectoryContext** out_dir_ctx);
64 bool AddFile(RomFSBuildDirectoryContext* parent_dir_ctx, RomFSBuildFileContext* file_ctx);
65
66public:
67 explicit RomFSBuildContext(VirtualDir base);
68
69 /* This finalizes the context. */
70 std::map<u64, VirtualFile> Build();
71};
72
73static inline RomFSDirectoryEntry* romfs_get_direntry(void* directories, uint32_t offset) {
74 return (RomFSDirectoryEntry*)((uintptr_t)directories + offset);
75}
76
77static inline RomFSFileEntry* romfs_get_fentry(void* files, uint32_t offset) {
78 return (RomFSFileEntry*)((uintptr_t)files + offset);
79}
80
81static inline uint32_t romfs_calc_path_hash(uint32_t parent, const unsigned char* path,
82 uint32_t start, size_t path_len) {
83 uint32_t hash = parent ^ 123456789;
84 for (uint32_t i = 0; i < path_len; i++) {
85 hash = (hash >> 5) | (hash << 27);
86 hash ^= path[start + i];
87 }
88
89 return hash;
90}
91
92static inline uint32_t romfs_get_hash_table_count(uint32_t num_entries) {
93 if (num_entries < 3) {
94 return 3;
95 } else if (num_entries < 19) {
96 return num_entries | 1;
97 }
98 uint32_t count = num_entries;
99 while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
100 count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
101 count++;
102 }
103 return count;
104}
105
106} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 74a0acf1a..af3f9a78f 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -68,33 +68,10 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
68 return exefs; 68 return exefs;
69} 69}
70 70
71VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, 71static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
72 ContentRecordType type) const {
73 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
74 static_cast<u8>(type));
75
76 if (romfs == nullptr)
77 return romfs;
78
79 const auto installed = Service::FileSystem::GetUnionContents();
80
81 // Game Updates
82 const auto update_tid = GetUpdateTitleID(title_id);
83 const auto update = installed->GetEntryRaw(update_tid, type);
84 if (update != nullptr) {
85 const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
86 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
87 new_nca->GetRomFS() != nullptr) {
88 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
89 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
90 romfs = new_nca->GetRomFS();
91 }
92 }
93
94 // LayeredFS
95 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 72 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
96 if (type == ContentRecordType::Program && load_dir != nullptr && load_dir->GetSize() > 0) { 73 if (type == ContentRecordType::Program && load_dir != nullptr && load_dir->GetSize() > 0) {
97 const auto extracted = ExtractRomFS(romfs); 74 auto extracted = ExtractRomFS(romfs);
98 75
99 if (extracted != nullptr) { 76 if (extracted != nullptr) {
100 auto patch_dirs = load_dir->GetSubdirectories(); 77 auto patch_dirs = load_dir->GetSubdirectories();
@@ -106,25 +83,52 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
106 std::vector<VirtualDir> layers; 83 std::vector<VirtualDir> layers;
107 layers.reserve(patch_dirs.size() + 1); 84 layers.reserve(patch_dirs.size() + 1);
108 for (const auto& subdir : patch_dirs) { 85 for (const auto& subdir : patch_dirs) {
109 const auto romfs_dir = subdir->GetSubdirectory("romfs"); 86 auto romfs_dir = subdir->GetSubdirectory("romfs");
110 if (romfs_dir != nullptr) 87 if (romfs_dir != nullptr)
111 layers.push_back(romfs_dir); 88 layers.push_back(std::move(romfs_dir));
112 } 89 }
113 90
114 layers.push_back(extracted); 91 layers.push_back(std::move(extracted));
115 92
116 const auto layered = LayerDirectories(layers); 93 const auto layered = LayerDirectories(layers);
117 94
118 if (layered != nullptr) { 95 if (layered != nullptr) {
119 const auto packed = CreateRomFS(layered); 96 auto packed = CreateRomFS(layered);
120 97
121 if (packed != nullptr) { 98 if (packed != nullptr) {
122 LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully"); 99 LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
123 romfs = packed; 100 romfs = std::move(packed);
124 } 101 }
125 } 102 }
126 } 103 }
127 } 104 }
105}
106
107VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
108 ContentRecordType type) const {
109 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
110 static_cast<u8>(type));
111
112 if (romfs == nullptr)
113 return romfs;
114
115 const auto installed = Service::FileSystem::GetUnionContents();
116
117 // Game Updates
118 const auto update_tid = GetUpdateTitleID(title_id);
119 const auto update = installed->GetEntryRaw(update_tid, type);
120 if (update != nullptr) {
121 const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
122 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
123 new_nca->GetRomFS() != nullptr) {
124 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
125 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
126 romfs = new_nca->GetRomFS();
127 }
128 }
129
130 // LayeredFS
131 ApplyLayeredFS(romfs, title_id, type);
128 132
129 return romfs; 133 return romfs;
130} 134}
@@ -153,7 +157,7 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
153 157
154 const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 158 const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
155 if (lfs_dir != nullptr && lfs_dir->GetSize() > 0) 159 if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
156 out[PatchType::LayeredFS] = ""; 160 out.insert_or_assign(PatchType::LayeredFS, "");
157 161
158 return out; 162 return out;
159} 163}
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 0dda0b861..653ef2e7b 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -18,6 +18,10 @@
18#include "core/loader/loader.h" 18#include "core/loader/loader.h"
19 19
20namespace FileSys { 20namespace FileSys {
21
22// The size of blocks to use when vfs raw copying into nand.
23constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
24
21std::string RegisteredCacheEntry::DebugInfo() const { 25std::string RegisteredCacheEntry::DebugInfo() const {
22 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); 26 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
23} 27}
@@ -121,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
121 if (concat.empty()) 125 if (concat.empty())
122 return nullptr; 126 return nullptr;
123 127
124 file = FileSys::ConcatenateFiles(concat); 128 file = FileSys::ConcatenateFiles(concat, concat.front()->GetName());
125 } 129 }
126 130
127 return file; 131 return file;
@@ -480,7 +484,8 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
480 auto out = dir->CreateFileRelative(path); 484 auto out = dir->CreateFileRelative(path);
481 if (out == nullptr) 485 if (out == nullptr)
482 return InstallResult::ErrorCopyFailed; 486 return InstallResult::ErrorCopyFailed;
483 return copy(in, out, 0x400000) ? InstallResult::Success : InstallResult::ErrorCopyFailed; 487 return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
488 : InstallResult::ErrorCopyFailed;
484} 489}
485 490
486bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { 491bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 71e4e0e2f..205284a4d 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -4,7 +4,7 @@
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/swap.h" 6#include "common/swap.h"
7#include "core/file_sys/fsmitm_romfsbuild.hpp" 7#include "core/file_sys/fsmitm_romfsbuild.h"
8#include "core/file_sys/romfs.h" 8#include "core/file_sys/romfs.h"
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/file_sys/vfs_concat.h" 10#include "core/file_sys/vfs_concat.h"
@@ -100,7 +100,7 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
100 } 100 }
101} 101}
102 102
103VirtualDir ExtractRomFS(VirtualFile file, bool traverse_into_data) { 103VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
104 RomFSHeader header{}; 104 RomFSHeader header{};
105 if (file->ReadObject(&header) != sizeof(RomFSHeader)) 105 if (file->ReadObject(&header) != sizeof(RomFSHeader))
106 return nullptr; 106 return nullptr;
@@ -119,8 +119,9 @@ VirtualDir ExtractRomFS(VirtualFile file, bool traverse_into_data) {
119 119
120 VirtualDir out = std::move(root); 120 VirtualDir out = std::move(root);
121 121
122 while (out->GetSubdirectories().size() == 1 && out->GetFiles().size() == 0) { 122 while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
123 if (out->GetSubdirectories().front()->GetName() == "data" && !traverse_into_data) 123 if (out->GetSubdirectories().front()->GetName() == "data" &&
124 type == RomFSExtractionType::Truncated)
124 break; 125 break;
125 out = out->GetSubdirectories().front(); 126 out = out->GetSubdirectories().front();
126 } 127 }
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index 8e82585a0..ecd1eb725 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -32,9 +32,15 @@ struct IVFCHeader {
32}; 32};
33static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); 33static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
34 34
35enum class RomFSExtractionType {
36 Full, // Includes data directory
37 Truncated, // Traverses into data directory
38};
39
35// Converts a RomFS binary blob to VFS Filesystem 40// Converts a RomFS binary blob to VFS Filesystem
36// Returns nullptr on failure 41// Returns nullptr on failure
37VirtualDir ExtractRomFS(VirtualFile file, bool traverse_into_data = true); 42VirtualDir ExtractRomFS(VirtualFile file,
43 RomFSExtractionType type = RomFSExtractionType::Truncated);
38 44
39// Converts a VFS filesystem into a RomFS binary 45// Converts a VFS filesystem into a RomFS binary
40// Returns nullptr on failure 46// Returns nullptr on failure
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 218cfde66..5fbea1739 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -399,8 +399,8 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
399 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); 399 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
400} 400}
401 401
402std::map<std::string, VfsEntryType> VfsDirectory::GetEntries() const { 402std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
403 std::map<std::string, VfsEntryType> out; 403 std::map<std::string, VfsEntryType, std::less<>> out;
404 for (const auto& dir : GetSubdirectories()) 404 for (const auto& dir : GetSubdirectories())
405 out.emplace(dir->GetName(), VfsEntryType::Directory); 405 out.emplace(dir->GetName(), VfsEntryType::Directory);
406 for (const auto& file : GetFiles()) 406 for (const auto& file : GetFiles())
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 6aec4c164..cea4aa8b8 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -268,7 +268,7 @@ public:
268 268
269 // Gets all of the entries directly in the directory (files and dirs), returning a map between 269 // Gets all of the entries directly in the directory (files and dirs), returning a map between
270 // item name -> type. 270 // item name -> type.
271 virtual std::map<std::string, VfsEntryType> GetEntries() const; 271 virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
272 272
273 // Interprets the file with name file instead as a directory of type directory. 273 // Interprets the file with name file instead as a directory of type directory.
274 // The directory must have a constructor that takes a single argument of type 274 // The directory must have a constructor that takes a single argument of type
@@ -323,6 +323,9 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block
323// directory of src/dest. 323// directory of src/dest.
324bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000); 324bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000);
325 325
326// A method that performs a similar function to VfsRawCopy above, but instead copies entire
327// directories. It suffers the same performance penalties as above and an implementation-specific
328// Copy should always be preferred.
326bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size = 0x1000); 329bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size = 0x1000);
327 330
328// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not 331// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 0c07e162e..d9f9911da 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -10,8 +10,9 @@
10 10
11namespace FileSys { 11namespace FileSys {
12 12
13bool VerifyConcatenationMap(std::map<u64, VirtualFile> map) { 13static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) {
14 for (auto iter = map.begin(); iter != --map.end();) { 14 const auto last_valid = --map.end();
15 for (auto iter = map.begin(); iter != last_valid;) {
15 const auto old = iter++; 16 const auto old = iter++;
16 if (old->first + old->second->GetSize() != iter->first) { 17 if (old->first + old->second->GetSize() != iter->first) {
17 return false; 18 return false;
@@ -41,9 +42,11 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::s
41 42
42ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name) 43ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
43 : files(std::move(files_)), name(std::move(name)) { 44 : files(std::move(files_)), name(std::move(name)) {
44 ASSERT(VerifyConcatenationMap(files)); 45 ASSERT(VerifyConcatenationMapContinuity(files));
45} 46}
46 47
48ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
49
47std::string ConcatenatedVfsFile::GetName() const { 50std::string ConcatenatedVfsFile::GetName() const {
48 if (files.empty()) 51 if (files.empty())
49 return ""; 52 return "";
@@ -77,25 +80,25 @@ bool ConcatenatedVfsFile::IsReadable() const {
77} 80}
78 81
79std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 82std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
80 std::pair<u64, VirtualFile> entry = *files.rbegin(); 83 auto entry = --files.end();
81 for (auto iter = files.begin(); iter != files.end(); ++iter) { 84 for (auto iter = files.begin(); iter != files.end(); ++iter) {
82 if (iter->first > offset) { 85 if (iter->first > offset) {
83 entry = *--iter; 86 entry = --iter;
84 break; 87 break;
85 } 88 }
86 } 89 }
87 90
88 if (entry.first + entry.second->GetSize() <= offset) 91 if (entry->first + entry->second->GetSize() <= offset)
89 return 0; 92 return 0;
90 93
91 const auto read_in = 94 const auto read_in =
92 std::min(entry.first + entry.second->GetSize() - offset, entry.second->GetSize()); 95 std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize());
93 if (length > read_in) { 96 if (length > read_in) {
94 return entry.second->Read(data, read_in, offset - entry.first) + 97 return entry->second->Read(data, read_in, offset - entry->first) +
95 Read(data + read_in, length - read_in, offset + read_in); 98 Read(data + read_in, length - read_in, offset + read_in);
96 } 99 }
97 100
98 return entry.second->Read(data, std::min(read_in, length), offset - entry.first); 101 return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first);
99} 102}
100 103
101std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 104std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index c65c20d15..76211d38a 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -13,35 +13,6 @@
13 13
14namespace FileSys { 14namespace FileSys {
15 15
16class ConcatenatedVfsFile;
17
18// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
19VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name = "");
20
21// Convenience function that turns a map of offsets to files into a concatenated file, filling gaps
22// with template parameter.
23template <u8 filler_byte>
24VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name = "") {
25 if (files.empty())
26 return nullptr;
27 if (files.size() == 1)
28 return files.begin()->second;
29
30 for (auto iter = files.begin(); iter != --files.end();) {
31 const auto old = iter++;
32 if (old->first + old->second->GetSize() != iter->first) {
33 files.emplace(old->first + old->second->GetSize(),
34 std::make_shared<StaticVfsFile<filler_byte>>(iter->first - old->first -
35 old->second->GetSize()));
36 }
37 }
38
39 if (files.begin()->first != 0)
40 files.emplace(0, std::make_shared<StaticVfsFile<filler_byte>>(files.begin()->first));
41
42 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
43}
44
45// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently 16// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
46// read-only. 17// read-only.
47class ConcatenatedVfsFile : public VfsFile { 18class ConcatenatedVfsFile : public VfsFile {
@@ -72,4 +43,33 @@ private:
72 std::string name; 43 std::string name;
73}; 44};
74 45
46// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
47VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
48
49// Convenience function that turns a map of offsets to files into a concatenated file, filling gaps
50// with template parameter.
51template <u8 filler_byte>
52VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name) {
53 if (files.empty())
54 return nullptr;
55 if (files.size() == 1)
56 return files.begin()->second;
57
58 const auto last_valid = --files.end();
59 for (auto iter = files.begin(); iter != last_valid;) {
60 const auto old = iter++;
61 if (old->first + old->second->GetSize() != iter->first) {
62 files.emplace(old->first + old->second->GetSize(),
63 std::make_shared<StaticVfsFile<filler_byte>>(iter->first - old->first -
64 old->second->GetSize()));
65 }
66 }
67
68 // Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
69 if (files.begin()->first != 0)
70 files.emplace(0, std::make_shared<StaticVfsFile<filler_byte>>(files.begin()->first));
71
72 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
73}
74
75} // namespace FileSys 75} // namespace FileSys
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index 802f49384..45563d7ae 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -20,10 +20,11 @@ VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) {
20LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name) 20LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
21 : dirs(std::move(dirs)), name(std::move(name)) {} 21 : dirs(std::move(dirs)), name(std::move(name)) {}
22 22
23LayeredVfsDirectory::~LayeredVfsDirectory() = default;
24
23std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const { 25std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
24 VirtualFile file;
25 for (const auto& layer : dirs) { 26 for (const auto& layer : dirs) {
26 file = layer->GetFileRelative(path); 27 const auto file = layer->GetFileRelative(path);
27 if (file != nullptr) 28 if (file != nullptr)
28 return file; 29 return file;
29 } 30 }
@@ -35,12 +36,12 @@ std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
35 std::string_view path) const { 36 std::string_view path) const {
36 std::vector<VirtualDir> out; 37 std::vector<VirtualDir> out;
37 for (const auto& layer : dirs) { 38 for (const auto& layer : dirs) {
38 const auto dir = layer->GetDirectoryRelative(path); 39 auto dir = layer->GetDirectoryRelative(path);
39 if (dir != nullptr) 40 if (dir != nullptr)
40 out.push_back(dir); 41 out.push_back(std::move(dir));
41 } 42 }
42 43
43 return LayerDirectories(out); 44 return LayerDirectories(std::move(out));
44} 45}
45 46
46std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const { 47std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
@@ -61,8 +62,9 @@ std::vector<std::shared_ptr<VfsFile>> LayeredVfsDirectory::GetFiles() const {
61 for (const auto& file : layer->GetFiles()) { 62 for (const auto& file : layer->GetFiles()) {
62 if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) { 63 if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
63 return comp->GetName() == file->GetName(); 64 return comp->GetName() == file->GetName();
64 }) == out.end()) 65 }) == out.end()) {
65 out.push_back(file); 66 out.push_back(file);
67 }
66 } 68 }
67 } 69 }
68 70
@@ -79,6 +81,7 @@ std::vector<std::shared_ptr<VfsDirectory>> LayeredVfsDirectory::GetSubdirectorie
79 } 81 }
80 82
81 std::vector<VirtualDir> out; 83 std::vector<VirtualDir> out;
84 out.reserve(names.size());
82 for (const auto& subdir : names) 85 for (const auto& subdir : names)
83 out.push_back(GetSubdirectory(subdir)); 86 out.push_back(GetSubdirectory(subdir));
84 87
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
index f345c2fb6..4f6e341ab 100644
--- a/src/core/file_sys/vfs_layered.h
+++ b/src/core/file_sys/vfs_layered.h
@@ -21,6 +21,8 @@ class LayeredVfsDirectory : public VfsDirectory {
21 LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name); 21 LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
22 22
23public: 23public:
24 ~LayeredVfsDirectory() override;
25
24 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; 26 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
25 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; 27 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
26 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; 28 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index a58a02de7..9defad04c 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -413,11 +413,11 @@ std::string RealVfsDirectory::GetFullPath() const {
413 return out; 413 return out;
414} 414}
415 415
416std::map<std::string, VfsEntryType> RealVfsDirectory::GetEntries() const { 416std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
417 if (perms == Mode::Append) 417 if (perms == Mode::Append)
418 return {}; 418 return {};
419 419
420 std::map<std::string, VfsEntryType> out; 420 std::map<std::string, VfsEntryType, std::less<>> out;
421 FileUtil::ForeachDirectoryEntry( 421 FileUtil::ForeachDirectoryEntry(
422 nullptr, path, 422 nullptr, path,
423 [&out](u64* entries_out, const std::string& directory, const std::string& filename) { 423 [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 3af2a6961..5b61db90d 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -98,7 +98,7 @@ public:
98 bool DeleteFile(std::string_view name) override; 98 bool DeleteFile(std::string_view name) override;
99 bool Rename(std::string_view name) override; 99 bool Rename(std::string_view name) override;
100 std::string GetFullPath() const override; 100 std::string GetFullPath() const override;
101 std::map<std::string, VfsEntryType> GetEntries() const override; 101 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
102 102
103protected: 103protected:
104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 5bc8ca52e..4dd47ffcc 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <memory> 8#include <memory>
8#include <string_view> 9#include <string_view>
9 10
@@ -15,7 +16,7 @@ template <u8 value>
15class StaticVfsFile : public VfsFile { 16class StaticVfsFile : public VfsFile {
16public: 17public:
17 explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr) 18 explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr)
18 : size(size), name(name), parent(parent) {} 19 : size(size), name(std::move(name)), parent(std::move(parent)) {}
19 20
20 std::string GetName() const override { 21 std::string GetName() const override {
21 return name; 22 return name;
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index efca3d4ad..7033e2c88 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring>
6#include <utility> 7#include <utility>
7#include "core/file_sys/vfs_vector.h" 8#include "core/file_sys/vfs_vector.h"
8 9
@@ -10,6 +11,8 @@ namespace FileSys {
10VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent) 11VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
11 : data(std::move(initial_data)), name(std::move(name)), parent(std::move(parent)) {} 12 : data(std::move(initial_data)), name(std::move(name)), parent(std::move(parent)) {}
12 13
14VectorVfsFile::~VectorVfsFile() = default;
15
13std::string VectorVfsFile::GetName() const { 16std::string VectorVfsFile::GetName() const {
14 return name; 17 return name;
15} 18}
@@ -20,7 +23,7 @@ size_t VectorVfsFile::GetSize() const {
20 23
21bool VectorVfsFile::Resize(size_t new_size) { 24bool VectorVfsFile::Resize(size_t new_size) {
22 data.resize(new_size); 25 data.resize(new_size);
23 return data.size() == new_size; 26 return true;
24} 27}
25 28
26std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const { 29std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const {
@@ -55,7 +58,7 @@ bool VectorVfsFile::Rename(std::string_view name_) {
55} 58}
56 59
57void VectorVfsFile::Assign(std::vector<u8> new_data) { 60void VectorVfsFile::Assign(std::vector<u8> new_data) {
58 data = new_data; 61 data = std::move(new_data);
59} 62}
60 63
61VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, 64VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index c84e137a9..115c3ae95 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -13,6 +13,7 @@ class VectorVfsFile : public VfsFile {
13public: 13public:
14 explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "", 14 explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "",
15 VirtualDir parent = nullptr); 15 VirtualDir parent = nullptr);
16 ~VectorVfsFile() override;
16 17
17 std::string GetName() const override; 18 std::string GetName() const override;
18 size_t GetSize() const override; 19 size_t GetSize() const override;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 7cfe8a32f..dc8b5407d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -784,7 +784,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
784 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) 784 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset())
785 : file; 785 : file;
786 786
787 const auto extracted = FileSys::ExtractRomFS(romfs, false); 787 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
788 if (extracted == nullptr) { 788 if (extracted == nullptr) {
789 failed(); 789 failed();
790 return; 790 return;