summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/file_sys/romfs.cpp3
-rw-r--r--src/core/file_sys/vfs_concat.cpp161
-rw-r--r--src/core/file_sys/vfs_concat.h28
4 files changed, 125 insertions, 70 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b5f62690e..4406ae30e 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -117,8 +117,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
117 return nullptr; 117 return nullptr;
118 } 118 }
119 119
120 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(std::move(concat), 120 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
121 dir->GetName());
122 } 121 }
123 122
124 if (Common::FS::IsDir(path)) { 123 if (Common::FS::IsDir(path)) {
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index ddcfe5980..fb5683a6b 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -140,7 +140,8 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
140 return nullptr; 140 return nullptr;
141 141
142 RomFSBuildContext ctx{dir, ext}; 142 RomFSBuildContext ctx{dir, ext};
143 return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); 143 auto file_map = ctx.Build();
144 return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
144} 145}
145 146
146} // namespace FileSys 147} // namespace FileSys
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index d23623aa0..853b893a1 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -10,84 +10,105 @@
10 10
11namespace FileSys { 11namespace FileSys {
12 12
13static bool VerifyConcatenationMapContinuity(const std::multimap<u64, VirtualFile>& map) { 13ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_)
14 const auto last_valid = --map.end(); 14 : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
15 for (auto iter = map.begin(); iter != last_valid;) { 15 DEBUG_ASSERT(this->VerifyContinuity());
16 const auto old = iter++; 16}
17 if (old->first + old->second->GetSize() != iter->first) { 17
18bool ConcatenatedVfsFile::VerifyContinuity() const {
19 u64 last_offset = 0;
20 for (auto& entry : concatenation_map) {
21 if (entry.offset != last_offset) {
18 return false; 22 return false;
19 } 23 }
20 }
21
22 return map.begin()->first == 0;
23}
24 24
25ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name_) 25 last_offset = entry.offset + entry.file->GetSize();
26 : name(std::move(name_)) {
27 std::size_t next_offset = 0;
28 for (const auto& file : files_) {
29 files.emplace(next_offset, file);
30 next_offset += file->GetSize();
31 } 26 }
32}
33 27
34ConcatenatedVfsFile::ConcatenatedVfsFile(std::multimap<u64, VirtualFile> files_, std::string name_) 28 return true;
35 : files(std::move(files_)), name(std::move(name_)) {
36 ASSERT(VerifyConcatenationMapContinuity(files));
37} 29}
38 30
39ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; 31ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
40 32
41VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files, 33VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files,
42 std::string name) { 34 std::string&& name) {
43 if (files.empty()) 35 // Fold trivial cases.
36 if (files.empty()) {
44 return nullptr; 37 return nullptr;
45 if (files.size() == 1) 38 }
46 return files[0]; 39 if (files.size() == 1) {
40 return files.front();
41 }
42
43 // Make the concatenation map from the input.
44 std::vector<ConcatenationEntry> concatenation_map;
45 concatenation_map.reserve(files.size());
46 u64 last_offset = 0;
47
48 for (auto& file : files) {
49 concatenation_map.emplace_back(ConcatenationEntry{
50 .offset = last_offset,
51 .file = file,
52 });
53
54 last_offset += file->GetSize();
55 }
47 56
48 return VirtualFile(new ConcatenatedVfsFile(std::move(files), std::move(name))); 57 return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
49} 58}
50 59
51VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, 60VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
52 std::multimap<u64, VirtualFile> files, 61 const std::multimap<u64, VirtualFile>& files,
53 std::string name) { 62 std::string&& name) {
54 if (files.empty()) 63 // Fold trivial cases.
64 if (files.empty()) {
55 return nullptr; 65 return nullptr;
56 if (files.size() == 1) 66 }
67 if (files.size() == 1) {
57 return files.begin()->second; 68 return files.begin()->second;
69 }
58 70
59 const auto last_valid = --files.end(); 71 // Make the concatenation map from the input.
60 for (auto iter = files.begin(); iter != last_valid;) { 72 std::vector<ConcatenationEntry> concatenation_map;
61 const auto old = iter++; 73
62 if (old->first + old->second->GetSize() != iter->first) { 74 concatenation_map.reserve(files.size());
63 files.emplace(old->first + old->second->GetSize(), 75 u64 last_offset = 0;
64 std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first - 76
65 old->second->GetSize())); 77 // Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
78 for (auto& [offset, file] : files) {
79 if (offset > last_offset) {
80 concatenation_map.emplace_back(ConcatenationEntry{
81 .offset = last_offset,
82 .file = std::make_shared<StaticVfsFile>(filler_byte, offset - last_offset),
83 });
66 } 84 }
67 }
68 85
69 // Ensure the map starts at offset 0 (start of file), otherwise pad to fill. 86 concatenation_map.emplace_back(ConcatenationEntry{
70 if (files.begin()->first != 0) 87 .offset = offset,
71 files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first)); 88 .file = file,
89 });
90
91 last_offset = offset + file->GetSize();
92 }
72 93
73 return VirtualFile(new ConcatenatedVfsFile(std::move(files), std::move(name))); 94 return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
74} 95}
75 96
76std::string ConcatenatedVfsFile::GetName() const { 97std::string ConcatenatedVfsFile::GetName() const {
77 if (files.empty()) { 98 if (concatenation_map.empty()) {
78 return ""; 99 return "";
79 } 100 }
80 if (!name.empty()) { 101 if (!name.empty()) {
81 return name; 102 return name;
82 } 103 }
83 return files.begin()->second->GetName(); 104 return concatenation_map.front().file->GetName();
84} 105}
85 106
86std::size_t ConcatenatedVfsFile::GetSize() const { 107std::size_t ConcatenatedVfsFile::GetSize() const {
87 if (files.empty()) { 108 if (concatenation_map.empty()) {
88 return 0; 109 return 0;
89 } 110 }
90 return files.rbegin()->first + files.rbegin()->second->GetSize(); 111 return concatenation_map.back().offset + concatenation_map.back().file->GetSize();
91} 112}
92 113
93bool ConcatenatedVfsFile::Resize(std::size_t new_size) { 114bool ConcatenatedVfsFile::Resize(std::size_t new_size) {
@@ -95,10 +116,10 @@ bool ConcatenatedVfsFile::Resize(std::size_t new_size) {
95} 116}
96 117
97VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const { 118VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const {
98 if (files.empty()) { 119 if (concatenation_map.empty()) {
99 return nullptr; 120 return nullptr;
100 } 121 }
101 return files.begin()->second->GetContainingDirectory(); 122 return concatenation_map.front().file->GetContainingDirectory();
102} 123}
103 124
104bool ConcatenatedVfsFile::IsWritable() const { 125bool ConcatenatedVfsFile::IsWritable() const {
@@ -110,25 +131,45 @@ bool ConcatenatedVfsFile::IsReadable() const {
110} 131}
111 132
112std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 133std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
113 auto entry = --files.end(); 134 const ConcatenationEntry key{
114 for (auto iter = files.begin(); iter != files.end(); ++iter) { 135 .offset = offset,
115 if (iter->first > offset) { 136 .file = nullptr,
116 entry = --iter; 137 };
138
139 // Read nothing if the map is empty.
140 if (concatenation_map.empty()) {
141 return 0;
142 }
143
144 // Binary search to find the iterator to the first position we can check.
145 // It must exist, since we are not empty and are comparing unsigned integers.
146 auto it = std::prev(std::upper_bound(concatenation_map.begin(), concatenation_map.end(), key));
147 u64 cur_length = length;
148 u64 cur_offset = offset;
149
150 while (cur_length > 0 && it != concatenation_map.end()) {
151 // Check if we can read the file at this position.
152 const auto& file = it->file;
153 const u64 file_offset = it->offset;
154 const u64 file_size = file->GetSize();
155
156 if (cur_offset >= file_offset + file_size) {
157 // Entirely out of bounds read.
117 break; 158 break;
118 } 159 }
119 }
120 160
121 if (entry->first + entry->second->GetSize() <= offset) 161 // Read the file at this position.
122 return 0; 162 const u64 intended_read_size = std::min<u64>(cur_length, file_size);
163 const u64 actual_read_size =
164 file->Read(data + (cur_offset - offset), intended_read_size, cur_offset - file_offset);
123 165
124 const auto read_in = 166 // Update tracking.
125 std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize()); 167 cur_offset += actual_read_size;
126 if (length > read_in) { 168 cur_length -= actual_read_size;
127 return entry->second->Read(data, read_in, offset - entry->first) + 169 it++;
128 Read(data + read_in, length - read_in, offset + read_in);
129 } 170 }
130 171
131 return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first); 172 return cur_offset - offset;
132} 173}
133 174
134std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 175std::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 9be0261b6..6b329d545 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <compare>
6#include <map> 7#include <map>
7#include <memory> 8#include <memory>
8#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
@@ -12,19 +13,33 @@ namespace FileSys {
12// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently 13// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
13// read-only. 14// read-only.
14class ConcatenatedVfsFile : public VfsFile { 15class ConcatenatedVfsFile : public VfsFile {
15 explicit ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name_); 16private:
16 explicit ConcatenatedVfsFile(std::multimap<u64, VirtualFile> files, std::string name_); 17 struct ConcatenationEntry {
18 u64 offset;
19 VirtualFile file;
20
21 auto operator<=>(const ConcatenationEntry& other) const {
22 return this->offset <=> other.offset;
23 }
24 };
25 using ConcatenationMap = std::vector<ConcatenationEntry>;
26
27 explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map,
28 std::string&& name);
29 bool VerifyContinuity() const;
17 30
18public: 31public:
19 ~ConcatenatedVfsFile() override; 32 ~ConcatenatedVfsFile() override;
20 33
21 /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. 34 /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
22 static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name); 35 static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files,
36 std::string&& name);
23 37
24 /// Convenience function that turns a map of offsets to files into a concatenated file, filling 38 /// Convenience function that turns a map of offsets to files into a concatenated file, filling
25 /// gaps with a given filler byte. 39 /// gaps with a given filler byte.
26 static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::multimap<u64, VirtualFile> files, 40 static VirtualFile MakeConcatenatedFile(u8 filler_byte,
27 std::string name); 41 const std::multimap<u64, VirtualFile>& files,
42 std::string&& name);
28 43
29 std::string GetName() const override; 44 std::string GetName() const override;
30 std::size_t GetSize() const override; 45 std::size_t GetSize() const override;
@@ -37,8 +52,7 @@ public:
37 bool Rename(std::string_view new_name) override; 52 bool Rename(std::string_view new_name) override;
38 53
39private: 54private:
40 // Maps starting offset to file -- more efficient. 55 ConcatenationMap concatenation_map;
41 std::multimap<u64, VirtualFile> files;
42 std::string name; 56 std::string name;
43}; 57};
44 58