summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp35
-rw-r--r--src/core/file_sys/patch_manager.cpp56
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/file_sys/romfs.cpp2
-rw-r--r--src/core/file_sys/vfs.cpp6
-rw-r--r--src/core/file_sys/vfs.h9
-rw-r--r--src/core/file_sys/vfs_concat.cpp45
-rw-r--r--src/core/file_sys/vfs_concat.h44
-rw-r--r--src/core/file_sys/vfs_layered.cpp15
-rw-r--r--src/core/file_sys/vfs_layered.h8
-rw-r--r--src/core/file_sys/vfs_static.h21
-rw-r--r--src/core/file_sys/vfs_vector.cpp6
-rw-r--r--src/core/file_sys/vfs_vector.h8
-rw-r--r--src/yuzu/main.cpp88
15 files changed, 172 insertions, 175 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 50f0a42fb..7666354dc 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -64,7 +64,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
64 if (concat.empty()) 64 if (concat.empty())
65 return nullptr; 65 return nullptr;
66 66
67 return FileSys::ConcatenateFiles(concat, dir->GetName()); 67 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
68 } 68 }
69 69
70 return vfs->OpenFile(path, FileSys::Mode::Read); 70 return vfs->OpenFile(path, FileSys::Mode::Read);
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index 21fc3d796..07b074817 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -73,7 +73,7 @@ static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size
73struct RomFSBuildFileContext; 73struct RomFSBuildFileContext;
74 74
75struct RomFSBuildDirectoryContext { 75struct RomFSBuildDirectoryContext {
76 std::string path = ""; 76 std::string path;
77 u32 cur_path_ofs = 0; 77 u32 cur_path_ofs = 0;
78 u32 path_len = 0; 78 u32 path_len = 0;
79 u32 entry_offset = 0; 79 u32 entry_offset = 0;
@@ -84,7 +84,7 @@ struct RomFSBuildDirectoryContext {
84}; 84};
85 85
86struct RomFSBuildFileContext { 86struct RomFSBuildFileContext {
87 std::string path = ""; 87 std::string path;
88 u32 cur_path_ofs = 0; 88 u32 cur_path_ofs = 0;
89 u32 path_len = 0; 89 u32 path_len = 0;
90 u32 entry_offset = 0; 90 u32 entry_offset = 0;
@@ -92,12 +92,10 @@ struct RomFSBuildFileContext {
92 u64 size = 0; 92 u64 size = 0;
93 std::shared_ptr<RomFSBuildDirectoryContext> parent; 93 std::shared_ptr<RomFSBuildDirectoryContext> parent;
94 std::shared_ptr<RomFSBuildFileContext> sibling; 94 std::shared_ptr<RomFSBuildFileContext> sibling;
95 VirtualFile source = nullptr; 95 VirtualFile source;
96
97 RomFSBuildFileContext() : path(""), cur_path_ofs(0), path_len(0) {}
98}; 96};
99 97
100static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t path_len) { 98static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
101 u32 hash = parent ^ 123456789; 99 u32 hash = parent ^ 123456789;
102 for (u32 i = 0; i < path_len; i++) { 100 for (u32 i = 0; i < path_len; i++) {
103 hash = (hash >> 5) | (hash << 27); 101 hash = (hash >> 5) | (hash << 27);
@@ -107,13 +105,16 @@ static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t
107 return hash; 105 return hash;
108} 106}
109 107
110static u32 romfs_get_hash_table_count(u32 num_entries) { 108static u64 romfs_get_hash_table_count(u64 num_entries) {
111 if (num_entries < 3) { 109 if (num_entries < 3) {
112 return 3; 110 return 3;
113 } else if (num_entries < 19) { 111 }
112
113 if (num_entries < 19) {
114 return num_entries | 1; 114 return num_entries | 1;
115 } 115 }
116 u32 count = num_entries; 116
117 u64 count = num_entries;
117 while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || 118 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 % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
119 count++; 120 count++;
@@ -139,7 +140,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
139 const auto child = std::make_shared<RomFSBuildDirectoryContext>(); 140 const auto child = std::make_shared<RomFSBuildDirectoryContext>();
140 // Set child's path. 141 // Set child's path.
141 child->cur_path_ofs = parent->path_len + 1; 142 child->cur_path_ofs = parent->path_len + 1;
142 child->path_len = child->cur_path_ofs + kv.first.size(); 143 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
143 child->path = parent->path + "/" + kv.first; 144 child->path = parent->path + "/" + kv.first;
144 145
145 // Sanity check on path_len 146 // Sanity check on path_len
@@ -152,7 +153,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
152 const auto child = std::make_shared<RomFSBuildFileContext>(); 153 const auto child = std::make_shared<RomFSBuildFileContext>();
153 // Set child's path. 154 // Set child's path.
154 child->cur_path_ofs = parent->path_len + 1; 155 child->cur_path_ofs = parent->path_len + 1;
155 child->path_len = child->cur_path_ofs + kv.first.size(); 156 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
156 child->path = parent->path + "/" + kv.first; 157 child->path = parent->path + "/" + kv.first;
157 158
158 // Sanity check on path_len 159 // Sanity check on path_len
@@ -219,8 +220,8 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_))
219RomFSBuildContext::~RomFSBuildContext() = default; 220RomFSBuildContext::~RomFSBuildContext() = default;
220 221
221std::map<u64, VirtualFile> RomFSBuildContext::Build() { 222std::map<u64, VirtualFile> RomFSBuildContext::Build() {
222 const auto dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs); 223 const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
223 const auto file_hash_table_entry_count = romfs_get_hash_table_count(num_files); 224 const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
224 dir_hash_table_size = 4 * dir_hash_table_entry_count; 225 dir_hash_table_size = 4 * dir_hash_table_entry_count;
225 file_hash_table_size = 4 * file_hash_table_entry_count; 226 file_hash_table_size = 4 * file_hash_table_entry_count;
226 227
@@ -233,12 +234,6 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
233 std::vector<u8> dir_table(dir_table_size); 234 std::vector<u8> dir_table(dir_table_size);
234 std::vector<u8> file_table(file_table_size); 235 std::vector<u8> file_table(file_table_size);
235 236
236 // Clear out hash tables.
237 for (u32 i = 0; i < dir_hash_table_entry_count; i++)
238 dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
239 for (u32 i = 0; i < file_hash_table_entry_count; i++)
240 file_hash_table[i] = ROMFS_ENTRY_EMPTY;
241
242 std::shared_ptr<RomFSBuildFileContext> cur_file; 237 std::shared_ptr<RomFSBuildFileContext> cur_file;
243 238
244 // Determine file offsets. 239 // Determine file offsets.
@@ -355,7 +350,7 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
355 350
356 std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size + 351 std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
357 dir_table_size); 352 dir_table_size);
358 auto index = 0; 353 std::size_t index = 0;
359 std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32)); 354 std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
360 index += dir_hash_table.size() * sizeof(u32); 355 index += dir_hash_table.size() * sizeof(u32);
361 std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size()); 356 std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index af3f9a78f..4b3b5e665 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -70,38 +70,40 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
70 70
71static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 71static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
72 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 72 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
73 if (type == ContentRecordType::Program && load_dir != nullptr && load_dir->GetSize() > 0) { 73 if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
74 auto extracted = ExtractRomFS(romfs); 74 return;
75 75 }
76 if (extracted != nullptr) {
77 auto patch_dirs = load_dir->GetSubdirectories();
78 std::sort(patch_dirs.begin(), patch_dirs.end(),
79 [](const VirtualDir& l, const VirtualDir& r) {
80 return l->GetName() < r->GetName();
81 });
82
83 std::vector<VirtualDir> layers;
84 layers.reserve(patch_dirs.size() + 1);
85 for (const auto& subdir : patch_dirs) {
86 auto romfs_dir = subdir->GetSubdirectory("romfs");
87 if (romfs_dir != nullptr)
88 layers.push_back(std::move(romfs_dir));
89 }
90 76
91 layers.push_back(std::move(extracted)); 77 auto extracted = ExtractRomFS(romfs);
78 if (extracted == nullptr) {
79 return;
80 }
92 81
93 const auto layered = LayerDirectories(layers); 82 auto patch_dirs = load_dir->GetSubdirectories();
83 std::sort(patch_dirs.begin(), patch_dirs.end(),
84 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
94 85
95 if (layered != nullptr) { 86 std::vector<VirtualDir> layers;
96 auto packed = CreateRomFS(layered); 87 layers.reserve(patch_dirs.size() + 1);
88 for (const auto& subdir : patch_dirs) {
89 auto romfs_dir = subdir->GetSubdirectory("romfs");
90 if (romfs_dir != nullptr)
91 layers.push_back(std::move(romfs_dir));
92 }
93 layers.push_back(std::move(extracted));
97 94
98 if (packed != nullptr) { 95 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
99 LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully"); 96 if (layered == nullptr) {
100 romfs = std::move(packed); 97 return;
101 } 98 }
102 } 99
103 } 100 auto packed = CreateRomFS(std::move(layered));
101 if (packed == nullptr) {
102 return;
104 } 103 }
104
105 LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
106 romfs = std::move(packed);
105} 107}
106 108
107VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, 109VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 653ef2e7b..e9b040689 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -125,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
125 if (concat.empty()) 125 if (concat.empty())
126 return nullptr; 126 return nullptr;
127 127
128 file = FileSys::ConcatenateFiles(concat, concat.front()->GetName()); 128 file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
129 } 129 }
130 130
131 return file; 131 return file;
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 205284a4d..5910f7046 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -134,7 +134,7 @@ VirtualFile CreateRomFS(VirtualDir dir) {
134 return nullptr; 134 return nullptr;
135 135
136 RomFSBuildContext ctx{dir}; 136 RomFSBuildContext ctx{dir};
137 return ConcatenateFiles<0>(ctx.Build(), dir->GetName()); 137 return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
138} 138}
139 139
140} // namespace FileSys 140} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 5fbea1739..bfe50da73 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -463,14 +463,14 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t
463 return true; 463 return true;
464} 464}
465 465
466bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size) { 466bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
467 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) 467 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
468 return false; 468 return false;
469 if (!dest->Resize(src->GetSize())) 469 if (!dest->Resize(src->GetSize()))
470 return false; 470 return false;
471 471
472 std::vector<u8> temp(std::min(block_size, src->GetSize())); 472 std::vector<u8> temp(std::min(block_size, src->GetSize()));
473 for (size_t i = 0; i < src->GetSize(); i += block_size) { 473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
474 const auto read = std::min(block_size, src->GetSize() - i); 474 const auto read = std::min(block_size, src->GetSize() - i);
475 const auto block = src->Read(temp.data(), read, i); 475 const auto block = src->Read(temp.data(), read, i);
476 476
@@ -481,7 +481,7 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_si
481 return true; 481 return true;
482} 482}
483 483
484bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size) { 484bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
485 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) 485 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
486 return false; 486 return false;
487 487
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index cea4aa8b8..270291631 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -315,18 +315,19 @@ public:
315 bool Rename(std::string_view name) override; 315 bool Rename(std::string_view name) override;
316}; 316};
317 317
318// Compare the two files, byte-for-byte, in increments specificed by block_size 318// Compare the two files, byte-for-byte, in increments specified by block_size
319bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x1000); 319bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
320 std::size_t block_size = 0x1000);
320 321
321// A method that copies the raw data between two different implementations of VirtualFile. If you 322// A method that copies the raw data between two different implementations of VirtualFile. If you
322// are using the same implementation, it is probably better to use the Copy method in the parent 323// are using the same implementation, it is probably better to use the Copy method in the parent
323// directory of src/dest. 324// directory of src/dest.
324bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000); 325bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
325 326
326// A method that performs a similar function to VfsRawCopy above, but instead copies entire 327// 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// directories. It suffers the same performance penalties as above and an implementation-specific
328// Copy should always be preferred. 329// Copy should always be preferred.
329bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size = 0x1000); 330bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
330 331
331// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not 332// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
332// it attempts to create it and returns the new dir or nullptr on failure. 333// it attempts to create it and returns the new dir or nullptr on failure.
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index d9f9911da..16d801c0c 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "core/file_sys/vfs_concat.h" 9#include "core/file_sys/vfs_concat.h"
10#include "core/file_sys/vfs_static.h"
10 11
11namespace FileSys { 12namespace FileSys {
12 13
@@ -22,15 +23,6 @@ static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& m
22 return map.begin()->first == 0; 23 return map.begin()->first == 0;
23} 24}
24 25
25VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
26 if (files.empty())
27 return nullptr;
28 if (files.size() == 1)
29 return files[0];
30
31 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
32}
33
34ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) 26ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
35 : name(std::move(name)) { 27 : name(std::move(name)) {
36 std::size_t next_offset = 0; 28 std::size_t next_offset = 0;
@@ -47,6 +39,41 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std:
47 39
48ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; 40ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
49 41
42VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files,
43 std::string name) {
44 if (files.empty())
45 return nullptr;
46 if (files.size() == 1)
47 return files[0];
48
49 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
50}
51
52VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
53 std::map<u64, VirtualFile> files,
54 std::string name) {
55 if (files.empty())
56 return nullptr;
57 if (files.size() == 1)
58 return files.begin()->second;
59
60 const auto last_valid = --files.end();
61 for (auto iter = files.begin(); iter != last_valid;) {
62 const auto old = iter++;
63 if (old->first + old->second->GetSize() != iter->first) {
64 files.emplace(old->first + old->second->GetSize(),
65 std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first -
66 old->second->GetSize()));
67 }
68 }
69
70 // Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
71 if (files.begin()->first != 0)
72 files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first));
73
74 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
75}
76
50std::string ConcatenatedVfsFile::GetName() const { 77std::string ConcatenatedVfsFile::GetName() const {
51 if (files.empty()) 78 if (files.empty())
52 return ""; 79 return "";
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index 76211d38a..c90f9d5d1 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -7,26 +7,27 @@
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9#include <string_view> 9#include <string_view>
10#include <boost/container/flat_map.hpp>
11#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
12#include "core/file_sys/vfs_static.h"
13 11
14namespace FileSys { 12namespace FileSys {
15 13
16// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently 14// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
17// read-only. 15// read-only.
18class ConcatenatedVfsFile : public VfsFile { 16class ConcatenatedVfsFile : public VfsFile {
19 friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
20
21 template <u8 filler_byte>
22 friend VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name);
23
24 ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name); 17 ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
25 ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name); 18 ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
26 19
27public: 20public:
28 ~ConcatenatedVfsFile() override; 21 ~ConcatenatedVfsFile() override;
29 22
23 /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
24 static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name);
25
26 /// Convenience function that turns a map of offsets to files into a concatenated file, filling
27 /// gaps with a given filler byte.
28 static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files,
29 std::string name);
30
30 std::string GetName() const override; 31 std::string GetName() const override;
31 std::size_t GetSize() const override; 32 std::size_t GetSize() const override;
32 bool Resize(std::size_t new_size) override; 33 bool Resize(std::size_t new_size) override;
@@ -43,33 +44,4 @@ private:
43 std::string name; 44 std::string name;
44}; 45};
45 46
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 47} // namespace FileSys
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index 45563d7ae..bfee01725 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -8,7 +8,13 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) { 11LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
12 : dirs(std::move(dirs)), name(std::move(name)) {}
13
14LayeredVfsDirectory::~LayeredVfsDirectory() = default;
15
16VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs,
17 std::string name) {
12 if (dirs.empty()) 18 if (dirs.empty())
13 return nullptr; 19 return nullptr;
14 if (dirs.size() == 1) 20 if (dirs.size() == 1)
@@ -17,11 +23,6 @@ VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) {
17 return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name))); 23 return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
18} 24}
19 25
20LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
21 : dirs(std::move(dirs)), name(std::move(name)) {}
22
23LayeredVfsDirectory::~LayeredVfsDirectory() = default;
24
25std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const { 26std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
26 for (const auto& layer : dirs) { 27 for (const auto& layer : dirs) {
27 const auto file = layer->GetFileRelative(path); 28 const auto file = layer->GetFileRelative(path);
@@ -41,7 +42,7 @@ std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
41 out.push_back(std::move(dir)); 42 out.push_back(std::move(dir));
42 } 43 }
43 44
44 return LayerDirectories(std::move(out)); 45 return MakeLayeredDirectory(std::move(out));
45} 46}
46 47
47std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const { 48std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
index 4f6e341ab..d85310f57 100644
--- a/src/core/file_sys/vfs_layered.h
+++ b/src/core/file_sys/vfs_layered.h
@@ -9,20 +9,18 @@
9 9
10namespace FileSys { 10namespace FileSys {
11 11
12// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
13VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name = "");
14
15// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first 12// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
16// one and falling back to the one after. The highest priority directory (overwrites all others) 13// one and falling back to the one after. The highest priority directory (overwrites all others)
17// should be element 0 in the dirs vector. 14// should be element 0 in the dirs vector.
18class LayeredVfsDirectory : public VfsDirectory { 15class LayeredVfsDirectory : public VfsDirectory {
19 friend VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name);
20
21 LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name); 16 LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
22 17
23public: 18public:
24 ~LayeredVfsDirectory() override; 19 ~LayeredVfsDirectory() override;
25 20
21 /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
22 static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
23
26 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; 24 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
27 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; 25 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
28 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; 26 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 4dd47ffcc..44fab51d1 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -12,21 +12,21 @@
12 12
13namespace FileSys { 13namespace FileSys {
14 14
15template <u8 value>
16class StaticVfsFile : public VfsFile { 15class StaticVfsFile : public VfsFile {
17public: 16public:
18 explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr) 17 explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "",
19 : size(size), name(std::move(name)), parent(std::move(parent)) {} 18 VirtualDir parent = nullptr)
19 : value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {}
20 20
21 std::string GetName() const override { 21 std::string GetName() const override {
22 return name; 22 return name;
23 } 23 }
24 24
25 size_t GetSize() const override { 25 std::size_t GetSize() const override {
26 return size; 26 return size;
27 } 27 }
28 28
29 bool Resize(size_t new_size) override { 29 bool Resize(std::size_t new_size) override {
30 size = new_size; 30 size = new_size;
31 return true; 31 return true;
32 } 32 }
@@ -43,23 +43,23 @@ public:
43 return true; 43 return true;
44 } 44 }
45 45
46 size_t Read(u8* data, size_t length, size_t offset) const override { 46 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override {
47 const auto read = std::min(length, size - offset); 47 const auto read = std::min(length, size - offset);
48 std::fill(data, data + read, value); 48 std::fill(data, data + read, value);
49 return read; 49 return read;
50 } 50 }
51 51
52 size_t Write(const u8* data, size_t length, size_t offset) override { 52 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
53 return 0; 53 return 0;
54 } 54 }
55 55
56 boost::optional<u8> ReadByte(size_t offset) const override { 56 boost::optional<u8> ReadByte(std::size_t offset) const override {
57 if (offset < size) 57 if (offset < size)
58 return value; 58 return value;
59 return boost::none; 59 return boost::none;
60 } 60 }
61 61
62 std::vector<u8> ReadBytes(size_t length, size_t offset) const override { 62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
63 const auto read = std::min(length, size - offset); 63 const auto read = std::min(length, size - offset);
64 return std::vector<u8>(read, value); 64 return std::vector<u8>(read, value);
65 } 65 }
@@ -70,7 +70,8 @@ public:
70 } 70 }
71 71
72private: 72private:
73 size_t size; 73 u8 value;
74 std::size_t size;
74 std::string name; 75 std::string name;
75 VirtualDir parent; 76 VirtualDir parent;
76}; 77};
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 7033e2c88..389c7e003 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -9,7 +9,7 @@
9 9
10namespace FileSys { 10namespace FileSys {
11VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent) 11VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
12 : data(std::move(initial_data)), name(std::move(name)), parent(std::move(parent)) {} 12 : data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {}
13 13
14VectorVfsFile::~VectorVfsFile() = default; 14VectorVfsFile::~VectorVfsFile() = default;
15 15
@@ -38,13 +38,13 @@ bool VectorVfsFile::IsReadable() const {
38 return true; 38 return true;
39} 39}
40 40
41size_t VectorVfsFile::Read(u8* data_, size_t length, size_t offset) const { 41std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const {
42 const auto read = std::min(length, data.size() - offset); 42 const auto read = std::min(length, data.size() - offset);
43 std::memcpy(data_, data.data() + offset, read); 43 std::memcpy(data_, data.data() + offset, read);
44 return read; 44 return read;
45} 45}
46 46
47size_t VectorVfsFile::Write(const u8* data_, size_t length, size_t offset) { 47std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) {
48 if (offset + length > data.size()) 48 if (offset + length > data.size())
49 data.resize(offset + length); 49 data.resize(offset + length);
50 const auto write = std::min(length, data.size() - offset); 50 const auto write = std::min(length, data.size() - offset);
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index 115c3ae95..48a414c98 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -16,13 +16,13 @@ public:
16 ~VectorVfsFile() override; 16 ~VectorVfsFile() override;
17 17
18 std::string GetName() const override; 18 std::string GetName() const override;
19 size_t GetSize() const override; 19 std::size_t GetSize() const override;
20 bool Resize(size_t new_size) override; 20 bool Resize(std::size_t new_size) override;
21 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; 21 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
22 bool IsWritable() const override; 22 bool IsWritable() const override;
23 bool IsReadable() const override; 23 bool IsReadable() const override;
24 size_t Read(u8* data, size_t length, size_t offset) const override; 24 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
25 size_t Write(const u8* data, size_t length, size_t offset) override; 25 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
26 bool Rename(std::string_view name) override; 26 bool Rename(std::string_view name) override;
27 27
28 virtual void Assign(std::vector<u8> new_data); 28 virtual void Assign(std::vector<u8> new_data);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index dc8b5407d..d74489935 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -756,11 +756,51 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
756 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); 756 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
757} 757}
758 758
759static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) {
760 std::size_t out = 0;
761
762 for (const auto& subdir : dir->GetSubdirectories()) {
763 out += 1 + CalculateRomFSEntrySize(subdir, full);
764 }
765
766 return out + (full ? dir->GetFiles().size() : 0);
767}
768
769static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src,
770 const FileSys::VirtualDir& dest, std::size_t block_size, bool full) {
771 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
772 return false;
773 if (dialog.wasCanceled())
774 return false;
775
776 if (full) {
777 for (const auto& file : src->GetFiles()) {
778 const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
779 if (!FileSys::VfsRawCopy(file, out, block_size))
780 return false;
781 dialog.setValue(dialog.value() + 1);
782 if (dialog.wasCanceled())
783 return false;
784 }
785 }
786
787 for (const auto& dir : src->GetSubdirectories()) {
788 const auto out = dest->CreateSubdirectory(dir->GetName());
789 if (!RomFSRawCopy(dialog, dir, out, block_size, full))
790 return false;
791 dialog.setValue(dialog.value() + 1);
792 if (dialog.wasCanceled())
793 return false;
794 }
795
796 return true;
797}
798
759void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 799void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
760 const auto path = fmt::format("{}{:016X}/romfs", 800 const auto path = fmt::format("{}{:016X}/romfs",
761 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id); 801 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
762 802
763 auto failed = [this, &path]() { 803 const auto failed = [this, &path] {
764 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 804 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
765 tr("There was an error copying the RomFS files or the user " 805 tr("There was an error copying the RomFS files or the user "
766 "cancelled the operation.")); 806 "cancelled the operation."));
@@ -808,53 +848,13 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
808 failed(); 848 failed();
809 849
810 const auto full = res == "Full"; 850 const auto full = res == "Full";
811 851 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
812 const static std::function<size_t(const FileSys::VirtualDir&, bool)> calculate_entry_size =
813 [](const FileSys::VirtualDir& dir, bool full) {
814 size_t out = 0;
815 for (const auto& subdir : dir->GetSubdirectories())
816 out += 1 + calculate_entry_size(subdir, full);
817 return out + full ? dir->GetFiles().size() : 0;
818 };
819 const auto entry_size = calculate_entry_size(extracted, full);
820 852
821 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this); 853 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this);
822 progress.setWindowModality(Qt::WindowModal); 854 progress.setWindowModality(Qt::WindowModal);
823 progress.setMinimumDuration(100); 855 progress.setMinimumDuration(100);
824 856
825 const static std::function<bool(QProgressDialog&, const FileSys::VirtualDir&, 857 if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) {
826 const FileSys::VirtualDir&, size_t, bool)>
827 qt_raw_copy = [](QProgressDialog& dialog, const FileSys::VirtualDir& src,
828 const FileSys::VirtualDir& dest, size_t block_size, bool full) {
829 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
830 return false;
831 if (dialog.wasCanceled())
832 return false;
833
834 if (full) {
835 for (const auto& file : src->GetFiles()) {
836 const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
837 if (!FileSys::VfsRawCopy(file, out, block_size))
838 return false;
839 dialog.setValue(dialog.value() + 1);
840 if (dialog.wasCanceled())
841 return false;
842 }
843 }
844
845 for (const auto& dir : src->GetSubdirectories()) {
846 const auto out = dest->CreateSubdirectory(dir->GetName());
847 if (!qt_raw_copy(dialog, dir, out, block_size, full))
848 return false;
849 dialog.setValue(dialog.value() + 1);
850 if (dialog.wasCanceled())
851 return false;
852 }
853
854 return true;
855 };
856
857 if (qt_raw_copy(progress, extracted, out, 0x400000, full)) {
858 progress.close(); 858 progress.close();
859 QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), 859 QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
860 tr("The operation completed successfully.")); 860 tr("The operation completed successfully."));
@@ -931,7 +931,7 @@ void GMainWindow::OnMenuInstallToNAND() {
931 } 931 }
932 932
933 const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, 933 const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
934 const FileSys::VirtualFile& dest, size_t block_size) { 934 const FileSys::VirtualFile& dest, std::size_t block_size) {
935 if (src == nullptr || dest == nullptr) 935 if (src == nullptr || dest == nullptr)
936 return false; 936 return false;
937 if (!dest->Resize(src->GetSize())) 937 if (!dest->Resize(src->GetSize()))