diff options
| author | 2024-01-16 06:23:01 +0100 | |
|---|---|---|
| committer | 2024-01-25 16:40:42 -0500 | |
| commit | 0f9288e38d80c6c63a545934557501fae40d3d83 (patch) | |
| tree | 0643100d2471a1545dbfb447319b6ea26fdd6b63 /src/core/file_sys/vfs | |
| parent | fs: Move fsp_srv subclasses to separate files (diff) | |
| download | yuzu-0f9288e38d80c6c63a545934557501fae40d3d83.tar.gz yuzu-0f9288e38d80c6c63a545934557501fae40d3d83.tar.xz yuzu-0f9288e38d80c6c63a545934557501fae40d3d83.zip | |
vfs: Move vfs files to their own directory
Diffstat (limited to 'src/core/file_sys/vfs')
| -rw-r--r-- | src/core/file_sys/vfs/vfs.cpp | 552 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs.h | 327 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_cached.cpp | 63 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_cached.h | 31 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_concat.cpp | 192 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_concat.h | 57 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_layered.cpp | 132 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_layered.h | 46 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_offset.cpp | 98 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_offset.h | 50 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_real.cpp | 528 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_real.h | 145 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_static.h | 80 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_types.h | 29 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_vector.cpp | 133 | ||||
| -rw-r--r-- | src/core/file_sys/vfs/vfs_vector.h | 131 |
16 files changed, 2594 insertions, 0 deletions
diff --git a/src/core/file_sys/vfs/vfs.cpp b/src/core/file_sys/vfs/vfs.cpp new file mode 100644 index 000000000..b88a5f91d --- /dev/null +++ b/src/core/file_sys/vfs/vfs.cpp | |||
| @@ -0,0 +1,552 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <numeric> | ||
| 6 | #include <string> | ||
| 7 | #include "common/fs/path_util.h" | ||
| 8 | #include "core/file_sys/mode.h" | ||
| 9 | #include "core/file_sys/vfs/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} | ||
| 14 | |||
| 15 | VfsFilesystem::~VfsFilesystem() = default; | ||
| 16 | |||
| 17 | std::string VfsFilesystem::GetName() const { | ||
| 18 | return root->GetName(); | ||
| 19 | } | ||
| 20 | |||
| 21 | bool VfsFilesystem::IsReadable() const { | ||
| 22 | return root->IsReadable(); | ||
| 23 | } | ||
| 24 | |||
| 25 | bool VfsFilesystem::IsWritable() const { | ||
| 26 | return root->IsWritable(); | ||
| 27 | } | ||
| 28 | |||
| 29 | VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { | ||
| 30 | const auto path = Common::FS::SanitizePath(path_); | ||
| 31 | if (root->GetFileRelative(path) != nullptr) | ||
| 32 | return VfsEntryType::File; | ||
| 33 | if (root->GetDirectoryRelative(path) != nullptr) | ||
| 34 | return VfsEntryType::Directory; | ||
| 35 | |||
| 36 | return VfsEntryType::None; | ||
| 37 | } | ||
| 38 | |||
| 39 | VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | ||
| 40 | const auto path = Common::FS::SanitizePath(path_); | ||
| 41 | return root->GetFileRelative(path); | ||
| 42 | } | ||
| 43 | |||
| 44 | VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { | ||
| 45 | const auto path = Common::FS::SanitizePath(path_); | ||
| 46 | return root->CreateFileRelative(path); | ||
| 47 | } | ||
| 48 | |||
| 49 | VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { | ||
| 50 | const auto old_path = Common::FS::SanitizePath(old_path_); | ||
| 51 | const auto new_path = Common::FS::SanitizePath(new_path_); | ||
| 52 | |||
| 53 | // VfsDirectory impls are only required to implement copy across the current directory. | ||
| 54 | if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { | ||
| 55 | if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) | ||
| 56 | return nullptr; | ||
| 57 | return OpenFile(new_path, Mode::ReadWrite); | ||
| 58 | } | ||
| 59 | |||
| 60 | // Do it using RawCopy. Non-default impls are encouraged to optimize this. | ||
| 61 | const auto old_file = OpenFile(old_path, Mode::Read); | ||
| 62 | if (old_file == nullptr) | ||
| 63 | return nullptr; | ||
| 64 | auto new_file = OpenFile(new_path, Mode::Read); | ||
| 65 | if (new_file != nullptr) | ||
| 66 | return nullptr; | ||
| 67 | new_file = CreateFile(new_path, Mode::Write); | ||
| 68 | if (new_file == nullptr) | ||
| 69 | return nullptr; | ||
| 70 | if (!VfsRawCopy(old_file, new_file)) | ||
| 71 | return nullptr; | ||
| 72 | return new_file; | ||
| 73 | } | ||
| 74 | |||
| 75 | VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { | ||
| 76 | const auto sanitized_old_path = Common::FS::SanitizePath(old_path); | ||
| 77 | const auto sanitized_new_path = Common::FS::SanitizePath(new_path); | ||
| 78 | |||
| 79 | // Again, non-default impls are highly encouraged to provide a more optimized version of this. | ||
| 80 | auto out = CopyFile(sanitized_old_path, sanitized_new_path); | ||
| 81 | if (out == nullptr) | ||
| 82 | return nullptr; | ||
| 83 | if (DeleteFile(sanitized_old_path)) | ||
| 84 | return out; | ||
| 85 | return nullptr; | ||
| 86 | } | ||
| 87 | |||
| 88 | bool VfsFilesystem::DeleteFile(std::string_view path_) { | ||
| 89 | const auto path = Common::FS::SanitizePath(path_); | ||
| 90 | auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); | ||
| 91 | if (parent == nullptr) | ||
| 92 | return false; | ||
| 93 | return parent->DeleteFile(Common::FS::GetFilename(path)); | ||
| 94 | } | ||
| 95 | |||
| 96 | VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { | ||
| 97 | const auto path = Common::FS::SanitizePath(path_); | ||
| 98 | return root->GetDirectoryRelative(path); | ||
| 99 | } | ||
| 100 | |||
| 101 | VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { | ||
| 102 | const auto path = Common::FS::SanitizePath(path_); | ||
| 103 | return root->CreateDirectoryRelative(path); | ||
| 104 | } | ||
| 105 | |||
| 106 | VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { | ||
| 107 | const auto old_path = Common::FS::SanitizePath(old_path_); | ||
| 108 | const auto new_path = Common::FS::SanitizePath(new_path_); | ||
| 109 | |||
| 110 | // Non-default impls are highly encouraged to provide a more optimized version of this. | ||
| 111 | auto old_dir = OpenDirectory(old_path, Mode::Read); | ||
| 112 | if (old_dir == nullptr) | ||
| 113 | return nullptr; | ||
| 114 | auto new_dir = OpenDirectory(new_path, Mode::Read); | ||
| 115 | if (new_dir != nullptr) | ||
| 116 | return nullptr; | ||
| 117 | new_dir = CreateDirectory(new_path, Mode::Write); | ||
| 118 | if (new_dir == nullptr) | ||
| 119 | return nullptr; | ||
| 120 | |||
| 121 | for (const auto& file : old_dir->GetFiles()) { | ||
| 122 | const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName()); | ||
| 123 | if (x == nullptr) | ||
| 124 | return nullptr; | ||
| 125 | } | ||
| 126 | |||
| 127 | for (const auto& dir : old_dir->GetSubdirectories()) { | ||
| 128 | const auto x = | ||
| 129 | CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName()); | ||
| 130 | if (x == nullptr) | ||
| 131 | return nullptr; | ||
| 132 | } | ||
| 133 | |||
| 134 | return new_dir; | ||
| 135 | } | ||
| 136 | |||
| 137 | VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { | ||
| 138 | const auto sanitized_old_path = Common::FS::SanitizePath(old_path); | ||
| 139 | const auto sanitized_new_path = Common::FS::SanitizePath(new_path); | ||
| 140 | |||
| 141 | // Non-default impls are highly encouraged to provide a more optimized version of this. | ||
| 142 | auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); | ||
| 143 | if (out == nullptr) | ||
| 144 | return nullptr; | ||
| 145 | if (DeleteDirectory(sanitized_old_path)) | ||
| 146 | return out; | ||
| 147 | return nullptr; | ||
| 148 | } | ||
| 149 | |||
| 150 | bool VfsFilesystem::DeleteDirectory(std::string_view path_) { | ||
| 151 | const auto path = Common::FS::SanitizePath(path_); | ||
| 152 | auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); | ||
| 153 | if (parent == nullptr) | ||
| 154 | return false; | ||
| 155 | return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); | ||
| 156 | } | ||
| 157 | |||
| 158 | VfsFile::~VfsFile() = default; | ||
| 159 | |||
| 160 | std::string VfsFile::GetExtension() const { | ||
| 161 | return std::string(Common::FS::GetExtensionFromFilename(GetName())); | ||
| 162 | } | ||
| 163 | |||
| 164 | VfsDirectory::~VfsDirectory() = default; | ||
| 165 | |||
| 166 | std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { | ||
| 167 | u8 out{}; | ||
| 168 | const std::size_t size = Read(&out, sizeof(u8), offset); | ||
| 169 | if (size == 1) { | ||
| 170 | return out; | ||
| 171 | } | ||
| 172 | |||
| 173 | return std::nullopt; | ||
| 174 | } | ||
| 175 | |||
| 176 | std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { | ||
| 177 | std::vector<u8> out(size); | ||
| 178 | std::size_t read_size = Read(out.data(), size, offset); | ||
| 179 | out.resize(read_size); | ||
| 180 | return out; | ||
| 181 | } | ||
| 182 | |||
| 183 | std::vector<u8> VfsFile::ReadAllBytes() const { | ||
| 184 | return ReadBytes(GetSize()); | ||
| 185 | } | ||
| 186 | |||
| 187 | bool VfsFile::WriteByte(u8 data, std::size_t offset) { | ||
| 188 | return Write(&data, 1, offset) == 1; | ||
| 189 | } | ||
| 190 | |||
| 191 | std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) { | ||
| 192 | return Write(data.data(), data.size(), offset); | ||
| 193 | } | ||
| 194 | |||
| 195 | std::string VfsFile::GetFullPath() const { | ||
| 196 | if (GetContainingDirectory() == nullptr) | ||
| 197 | return '/' + GetName(); | ||
| 198 | |||
| 199 | return GetContainingDirectory()->GetFullPath() + '/' + GetName(); | ||
| 200 | } | ||
| 201 | |||
| 202 | VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { | ||
| 203 | auto vec = Common::FS::SplitPathComponents(path); | ||
| 204 | if (vec.empty()) { | ||
| 205 | return nullptr; | ||
| 206 | } | ||
| 207 | |||
| 208 | if (vec.size() == 1) { | ||
| 209 | return GetFile(vec[0]); | ||
| 210 | } | ||
| 211 | |||
| 212 | auto dir = GetSubdirectory(vec[0]); | ||
| 213 | for (std::size_t component = 1; component < vec.size() - 1; ++component) { | ||
| 214 | if (dir == nullptr) { | ||
| 215 | return nullptr; | ||
| 216 | } | ||
| 217 | |||
| 218 | dir = dir->GetSubdirectory(vec[component]); | ||
| 219 | } | ||
| 220 | |||
| 221 | if (dir == nullptr) { | ||
| 222 | return nullptr; | ||
| 223 | } | ||
| 224 | |||
| 225 | return dir->GetFile(vec.back()); | ||
| 226 | } | ||
| 227 | |||
| 228 | VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const { | ||
| 229 | if (IsRoot()) { | ||
| 230 | return GetFileRelative(path); | ||
| 231 | } | ||
| 232 | |||
| 233 | return GetParentDirectory()->GetFileAbsolute(path); | ||
| 234 | } | ||
| 235 | |||
| 236 | VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const { | ||
| 237 | auto vec = Common::FS::SplitPathComponents(path); | ||
| 238 | if (vec.empty()) { | ||
| 239 | // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently | ||
| 240 | // because of const-ness | ||
| 241 | return nullptr; | ||
| 242 | } | ||
| 243 | |||
| 244 | auto dir = GetSubdirectory(vec[0]); | ||
| 245 | for (std::size_t component = 1; component < vec.size(); ++component) { | ||
| 246 | if (dir == nullptr) { | ||
| 247 | return nullptr; | ||
| 248 | } | ||
| 249 | |||
| 250 | dir = dir->GetSubdirectory(vec[component]); | ||
| 251 | } | ||
| 252 | |||
| 253 | return dir; | ||
| 254 | } | ||
| 255 | |||
| 256 | VirtualDir VfsDirectory::GetDirectoryAbsolute(std::string_view path) const { | ||
| 257 | if (IsRoot()) { | ||
| 258 | return GetDirectoryRelative(path); | ||
| 259 | } | ||
| 260 | |||
| 261 | return GetParentDirectory()->GetDirectoryAbsolute(path); | ||
| 262 | } | ||
| 263 | |||
| 264 | VirtualFile VfsDirectory::GetFile(std::string_view name) const { | ||
| 265 | const auto& files = GetFiles(); | ||
| 266 | const auto iter = std::find_if(files.begin(), files.end(), | ||
| 267 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 268 | return iter == files.end() ? nullptr : *iter; | ||
| 269 | } | ||
| 270 | |||
| 271 | FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const { | ||
| 272 | return {}; | ||
| 273 | } | ||
| 274 | |||
| 275 | VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const { | ||
| 276 | const auto& subs = GetSubdirectories(); | ||
| 277 | const auto iter = std::find_if(subs.begin(), subs.end(), | ||
| 278 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 279 | return iter == subs.end() ? nullptr : *iter; | ||
| 280 | } | ||
| 281 | |||
| 282 | bool VfsDirectory::IsRoot() const { | ||
| 283 | return GetParentDirectory() == nullptr; | ||
| 284 | } | ||
| 285 | |||
| 286 | std::size_t VfsDirectory::GetSize() const { | ||
| 287 | const auto& files = GetFiles(); | ||
| 288 | const auto sum_sizes = [](const auto& range) { | ||
| 289 | return std::accumulate(range.begin(), range.end(), 0ULL, | ||
| 290 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 291 | }; | ||
| 292 | |||
| 293 | const auto file_total = sum_sizes(files); | ||
| 294 | const auto& sub_dir = GetSubdirectories(); | ||
| 295 | const auto subdir_total = sum_sizes(sub_dir); | ||
| 296 | |||
| 297 | return file_total + subdir_total; | ||
| 298 | } | ||
| 299 | |||
| 300 | VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) { | ||
| 301 | auto vec = Common::FS::SplitPathComponents(path); | ||
| 302 | if (vec.empty()) { | ||
| 303 | return nullptr; | ||
| 304 | } | ||
| 305 | |||
| 306 | if (vec.size() == 1) { | ||
| 307 | return CreateFile(vec[0]); | ||
| 308 | } | ||
| 309 | |||
| 310 | auto dir = GetSubdirectory(vec[0]); | ||
| 311 | if (dir == nullptr) { | ||
| 312 | dir = CreateSubdirectory(vec[0]); | ||
| 313 | if (dir == nullptr) { | ||
| 314 | return nullptr; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path)); | ||
| 319 | } | ||
| 320 | |||
| 321 | VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) { | ||
| 322 | if (IsRoot()) { | ||
| 323 | return CreateFileRelative(path); | ||
| 324 | } | ||
| 325 | |||
| 326 | return GetParentDirectory()->CreateFileAbsolute(path); | ||
| 327 | } | ||
| 328 | |||
| 329 | VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) { | ||
| 330 | auto vec = Common::FS::SplitPathComponents(path); | ||
| 331 | if (vec.empty()) { | ||
| 332 | return nullptr; | ||
| 333 | } | ||
| 334 | |||
| 335 | if (vec.size() == 1) { | ||
| 336 | return CreateSubdirectory(vec[0]); | ||
| 337 | } | ||
| 338 | |||
| 339 | auto dir = GetSubdirectory(vec[0]); | ||
| 340 | if (dir == nullptr) { | ||
| 341 | dir = CreateSubdirectory(vec[0]); | ||
| 342 | if (dir == nullptr) { | ||
| 343 | return nullptr; | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path)); | ||
| 348 | } | ||
| 349 | |||
| 350 | VirtualDir VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { | ||
| 351 | if (IsRoot()) { | ||
| 352 | return CreateDirectoryRelative(path); | ||
| 353 | } | ||
| 354 | |||
| 355 | return GetParentDirectory()->CreateDirectoryAbsolute(path); | ||
| 356 | } | ||
| 357 | |||
| 358 | bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { | ||
| 359 | auto dir = GetSubdirectory(name); | ||
| 360 | if (dir == nullptr) { | ||
| 361 | return false; | ||
| 362 | } | ||
| 363 | |||
| 364 | bool success = true; | ||
| 365 | for (const auto& file : dir->GetFiles()) { | ||
| 366 | if (!DeleteFile(file->GetName())) { | ||
| 367 | success = false; | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 372 | if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { | ||
| 373 | success = false; | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | return success; | ||
| 378 | } | ||
| 379 | |||
| 380 | bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { | ||
| 381 | auto dir = GetSubdirectory(name); | ||
| 382 | if (dir == nullptr) { | ||
| 383 | return false; | ||
| 384 | } | ||
| 385 | |||
| 386 | bool success = true; | ||
| 387 | for (const auto& file : dir->GetFiles()) { | ||
| 388 | if (!dir->DeleteFile(file->GetName())) { | ||
| 389 | success = false; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 394 | if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { | ||
| 395 | success = false; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | return success; | ||
| 400 | } | ||
| 401 | |||
| 402 | bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { | ||
| 403 | const auto f1 = GetFile(src); | ||
| 404 | auto f2 = CreateFile(dest); | ||
| 405 | if (f1 == nullptr || f2 == nullptr) { | ||
| 406 | return false; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (!f2->Resize(f1->GetSize())) { | ||
| 410 | DeleteFile(dest); | ||
| 411 | return false; | ||
| 412 | } | ||
| 413 | |||
| 414 | return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); | ||
| 415 | } | ||
| 416 | |||
| 417 | std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const { | ||
| 418 | std::map<std::string, VfsEntryType, std::less<>> out; | ||
| 419 | for (const auto& dir : GetSubdirectories()) | ||
| 420 | out.emplace(dir->GetName(), VfsEntryType::Directory); | ||
| 421 | for (const auto& file : GetFiles()) | ||
| 422 | out.emplace(file->GetName(), VfsEntryType::File); | ||
| 423 | return out; | ||
| 424 | } | ||
| 425 | |||
| 426 | std::string VfsDirectory::GetFullPath() const { | ||
| 427 | if (IsRoot()) | ||
| 428 | return GetName(); | ||
| 429 | |||
| 430 | return GetParentDirectory()->GetFullPath() + '/' + GetName(); | ||
| 431 | } | ||
| 432 | |||
| 433 | bool ReadOnlyVfsDirectory::IsWritable() const { | ||
| 434 | return false; | ||
| 435 | } | ||
| 436 | |||
| 437 | bool ReadOnlyVfsDirectory::IsReadable() const { | ||
| 438 | return true; | ||
| 439 | } | ||
| 440 | |||
| 441 | VirtualDir ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) { | ||
| 442 | return nullptr; | ||
| 443 | } | ||
| 444 | |||
| 445 | VirtualFile ReadOnlyVfsDirectory::CreateFile(std::string_view name) { | ||
| 446 | return nullptr; | ||
| 447 | } | ||
| 448 | |||
| 449 | VirtualFile ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) { | ||
| 450 | return nullptr; | ||
| 451 | } | ||
| 452 | |||
| 453 | VirtualFile ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) { | ||
| 454 | return nullptr; | ||
| 455 | } | ||
| 456 | |||
| 457 | VirtualDir ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) { | ||
| 458 | return nullptr; | ||
| 459 | } | ||
| 460 | |||
| 461 | VirtualDir ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) { | ||
| 462 | return nullptr; | ||
| 463 | } | ||
| 464 | |||
| 465 | bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) { | ||
| 466 | return false; | ||
| 467 | } | ||
| 468 | |||
| 469 | bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { | ||
| 470 | return false; | ||
| 471 | } | ||
| 472 | |||
| 473 | bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { | ||
| 474 | return false; | ||
| 475 | } | ||
| 476 | |||
| 477 | bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { | ||
| 478 | return false; | ||
| 479 | } | ||
| 480 | |||
| 481 | bool ReadOnlyVfsDirectory::Rename(std::string_view name) { | ||
| 482 | return false; | ||
| 483 | } | ||
| 484 | |||
| 485 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) { | ||
| 486 | if (file1->GetSize() != file2->GetSize()) | ||
| 487 | return false; | ||
| 488 | |||
| 489 | std::vector<u8> f1_v(block_size); | ||
| 490 | std::vector<u8> f2_v(block_size); | ||
| 491 | for (std::size_t i = 0; i < file1->GetSize(); i += block_size) { | ||
| 492 | auto f1_vs = file1->Read(f1_v.data(), block_size, i); | ||
| 493 | auto f2_vs = file2->Read(f2_v.data(), block_size, i); | ||
| 494 | |||
| 495 | if (f1_vs != f2_vs) | ||
| 496 | return false; | ||
| 497 | auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); | ||
| 498 | if (iters.first != f1_v.end() && iters.second != f2_v.end()) | ||
| 499 | return false; | ||
| 500 | } | ||
| 501 | |||
| 502 | return true; | ||
| 503 | } | ||
| 504 | |||
| 505 | bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) { | ||
| 506 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 507 | return false; | ||
| 508 | if (!dest->Resize(src->GetSize())) | ||
| 509 | return false; | ||
| 510 | |||
| 511 | std::vector<u8> temp(std::min(block_size, src->GetSize())); | ||
| 512 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { | ||
| 513 | const auto read = std::min(block_size, src->GetSize() - i); | ||
| 514 | |||
| 515 | if (src->Read(temp.data(), read, i) != read) { | ||
| 516 | return false; | ||
| 517 | } | ||
| 518 | |||
| 519 | if (dest->Write(temp.data(), read, i) != read) { | ||
| 520 | return false; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | return true; | ||
| 525 | } | ||
| 526 | |||
| 527 | bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) { | ||
| 528 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 529 | return false; | ||
| 530 | |||
| 531 | for (const auto& file : src->GetFiles()) { | ||
| 532 | const auto out = dest->CreateFile(file->GetName()); | ||
| 533 | if (!VfsRawCopy(file, out, block_size)) | ||
| 534 | return false; | ||
| 535 | } | ||
| 536 | |||
| 537 | for (const auto& dir : src->GetSubdirectories()) { | ||
| 538 | const auto out = dest->CreateSubdirectory(dir->GetName()); | ||
| 539 | if (!VfsRawCopyD(dir, out, block_size)) | ||
| 540 | return false; | ||
| 541 | } | ||
| 542 | |||
| 543 | return true; | ||
| 544 | } | ||
| 545 | |||
| 546 | VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { | ||
| 547 | const auto res = rel->GetDirectoryRelative(path); | ||
| 548 | if (res == nullptr) | ||
| 549 | return rel->CreateDirectoryRelative(path); | ||
| 550 | return res; | ||
| 551 | } | ||
| 552 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs.h b/src/core/file_sys/vfs/vfs.h new file mode 100644 index 000000000..6830244e3 --- /dev/null +++ b/src/core/file_sys/vfs/vfs.h | |||
| @@ -0,0 +1,327 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <functional> | ||
| 7 | #include <map> | ||
| 8 | #include <memory> | ||
| 9 | #include <optional> | ||
| 10 | #include <string> | ||
| 11 | #include <type_traits> | ||
| 12 | #include <vector> | ||
| 13 | |||
| 14 | #include "common/common_funcs.h" | ||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "core/file_sys/vfs/vfs_types.h" | ||
| 17 | |||
| 18 | namespace FileSys { | ||
| 19 | |||
| 20 | enum class Mode : u32; | ||
| 21 | |||
| 22 | // An enumeration representing what can be at the end of a path in a VfsFilesystem | ||
| 23 | enum class VfsEntryType { | ||
| 24 | None, | ||
| 25 | File, | ||
| 26 | Directory, | ||
| 27 | }; | ||
| 28 | |||
| 29 | // A class representing an abstract filesystem. A default implementation given the root VirtualDir | ||
| 30 | // is provided for convenience, but if the Vfs implementation has any additional state or | ||
| 31 | // functionality, they will need to override. | ||
| 32 | class VfsFilesystem { | ||
| 33 | public: | ||
| 34 | YUZU_NON_COPYABLE(VfsFilesystem); | ||
| 35 | YUZU_NON_MOVEABLE(VfsFilesystem); | ||
| 36 | |||
| 37 | explicit VfsFilesystem(VirtualDir root); | ||
| 38 | virtual ~VfsFilesystem(); | ||
| 39 | |||
| 40 | // Gets the friendly name for the filesystem. | ||
| 41 | virtual std::string GetName() const; | ||
| 42 | |||
| 43 | // Return whether or not the user has read permissions on this filesystem. | ||
| 44 | virtual bool IsReadable() const; | ||
| 45 | // Return whether or not the user has write permission on this filesystem. | ||
| 46 | virtual bool IsWritable() const; | ||
| 47 | |||
| 48 | // Determine if the entry at path is non-existent, a file, or a directory. | ||
| 49 | virtual VfsEntryType GetEntryType(std::string_view path) const; | ||
| 50 | |||
| 51 | // Opens the file with path relative to root. If it doesn't exist, returns nullptr. | ||
| 52 | virtual VirtualFile OpenFile(std::string_view path, Mode perms); | ||
| 53 | // Creates a new, empty file at path | ||
| 54 | virtual VirtualFile CreateFile(std::string_view path, Mode perms); | ||
| 55 | // Copies the file from old_path to new_path, returning the new file on success and nullptr on | ||
| 56 | // failure. | ||
| 57 | virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); | ||
| 58 | // Moves the file from old_path to new_path, returning the moved file on success and nullptr on | ||
| 59 | // failure. | ||
| 60 | virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); | ||
| 61 | // Deletes the file with path relative to root, returning true on success. | ||
| 62 | virtual bool DeleteFile(std::string_view path); | ||
| 63 | |||
| 64 | // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. | ||
| 65 | virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); | ||
| 66 | // Creates a new, empty directory at path | ||
| 67 | virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); | ||
| 68 | // Copies the directory from old_path to new_path, returning the new directory on success and | ||
| 69 | // nullptr on failure. | ||
| 70 | virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); | ||
| 71 | // Moves the directory from old_path to new_path, returning the moved directory on success and | ||
| 72 | // nullptr on failure. | ||
| 73 | virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); | ||
| 74 | // Deletes the directory with path relative to root, returning true on success. | ||
| 75 | virtual bool DeleteDirectory(std::string_view path); | ||
| 76 | |||
| 77 | protected: | ||
| 78 | // Root directory in default implementation. | ||
| 79 | VirtualDir root; | ||
| 80 | }; | ||
| 81 | |||
| 82 | // A class representing a file in an abstract filesystem. | ||
| 83 | class VfsFile { | ||
| 84 | public: | ||
| 85 | YUZU_NON_COPYABLE(VfsFile); | ||
| 86 | YUZU_NON_MOVEABLE(VfsFile); | ||
| 87 | |||
| 88 | VfsFile() = default; | ||
| 89 | virtual ~VfsFile(); | ||
| 90 | |||
| 91 | // Retrieves the file name. | ||
| 92 | virtual std::string GetName() const = 0; | ||
| 93 | // Retrieves the extension of the file name. | ||
| 94 | virtual std::string GetExtension() const; | ||
| 95 | // Retrieves the size of the file. | ||
| 96 | virtual std::size_t GetSize() const = 0; | ||
| 97 | // Resizes the file to new_size. Returns whether or not the operation was successful. | ||
| 98 | virtual bool Resize(std::size_t new_size) = 0; | ||
| 99 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. | ||
| 100 | virtual VirtualDir GetContainingDirectory() const = 0; | ||
| 101 | |||
| 102 | // Returns whether or not the file can be written to. | ||
| 103 | virtual bool IsWritable() const = 0; | ||
| 104 | // Returns whether or not the file can be read from. | ||
| 105 | virtual bool IsReadable() const = 0; | ||
| 106 | |||
| 107 | // The primary method of reading from the file. Reads length bytes into data starting at offset | ||
| 108 | // into file. Returns number of bytes successfully read. | ||
| 109 | virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0; | ||
| 110 | // The primary method of writing to the file. Writes length bytes from data starting at offset | ||
| 111 | // into file. Returns number of bytes successfully written. | ||
| 112 | virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; | ||
| 113 | |||
| 114 | // Reads exactly one byte at the offset provided, returning std::nullopt on error. | ||
| 115 | virtual std::optional<u8> ReadByte(std::size_t offset = 0) const; | ||
| 116 | // Reads size bytes starting at offset in file into a vector. | ||
| 117 | virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; | ||
| 118 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), | ||
| 119 | // 0)' | ||
| 120 | virtual std::vector<u8> ReadAllBytes() const; | ||
| 121 | |||
| 122 | // Reads an array of type T, size number_elements starting at offset. | ||
| 123 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. | ||
| 124 | template <typename T> | ||
| 125 | std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const { | ||
| 126 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 127 | |||
| 128 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); | ||
| 129 | } | ||
| 130 | |||
| 131 | // Reads size bytes into the memory starting at data starting at offset into the file. | ||
| 132 | // Returns the number of bytes read successfully. | ||
| 133 | template <typename T> | ||
| 134 | std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const { | ||
| 135 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 136 | return Read(reinterpret_cast<u8*>(data), size, offset); | ||
| 137 | } | ||
| 138 | |||
| 139 | // Reads one object of type T starting at offset in file. | ||
| 140 | // Returns the number of bytes read successfully (sizeof(T)). | ||
| 141 | template <typename T> | ||
| 142 | std::size_t ReadObject(T* data, std::size_t offset = 0) const { | ||
| 143 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 144 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); | ||
| 145 | } | ||
| 146 | |||
| 147 | // Writes exactly one byte to offset in file and returns whether or not the byte was written | ||
| 148 | // successfully. | ||
| 149 | virtual bool WriteByte(u8 data, std::size_t offset = 0); | ||
| 150 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully | ||
| 151 | // written. | ||
| 152 | virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0); | ||
| 153 | |||
| 154 | // Writes an array of type T, size number_elements to offset in file. | ||
| 155 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. | ||
| 156 | template <typename T> | ||
| 157 | std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { | ||
| 158 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 159 | return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset); | ||
| 160 | } | ||
| 161 | |||
| 162 | // Writes size bytes starting at memory location data to offset in file. | ||
| 163 | // Returns the number of bytes written successfully. | ||
| 164 | template <typename T> | ||
| 165 | std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) { | ||
| 166 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 167 | return Write(reinterpret_cast<const u8*>(data), size, offset); | ||
| 168 | } | ||
| 169 | |||
| 170 | // Writes one object of type T to offset in file. | ||
| 171 | // Returns the number of bytes written successfully (sizeof(T)). | ||
| 172 | template <typename T> | ||
| 173 | std::size_t WriteObject(const T& data, std::size_t offset = 0) { | ||
| 174 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 175 | return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset); | ||
| 176 | } | ||
| 177 | |||
| 178 | // Renames the file to name. Returns whether or not the operation was successful. | ||
| 179 | virtual bool Rename(std::string_view name) = 0; | ||
| 180 | |||
| 181 | // Returns the full path of this file as a string, recursively | ||
| 182 | virtual std::string GetFullPath() const; | ||
| 183 | }; | ||
| 184 | |||
| 185 | // A class representing a directory in an abstract filesystem. | ||
| 186 | class VfsDirectory { | ||
| 187 | public: | ||
| 188 | YUZU_NON_COPYABLE(VfsDirectory); | ||
| 189 | YUZU_NON_MOVEABLE(VfsDirectory); | ||
| 190 | |||
| 191 | VfsDirectory() = default; | ||
| 192 | virtual ~VfsDirectory(); | ||
| 193 | |||
| 194 | // Retrieves the file located at path as if the current directory was root. Returns nullptr if | ||
| 195 | // not found. | ||
| 196 | virtual VirtualFile GetFileRelative(std::string_view path) const; | ||
| 197 | // Calls GetFileRelative(path) on the root of the current directory. | ||
| 198 | virtual VirtualFile GetFileAbsolute(std::string_view path) const; | ||
| 199 | |||
| 200 | // Retrieves the directory located at path as if the current directory was root. Returns nullptr | ||
| 201 | // if not found. | ||
| 202 | virtual VirtualDir GetDirectoryRelative(std::string_view path) const; | ||
| 203 | // Calls GetDirectoryRelative(path) on the root of the current directory. | ||
| 204 | virtual VirtualDir GetDirectoryAbsolute(std::string_view path) const; | ||
| 205 | |||
| 206 | // Returns a vector containing all of the files in this directory. | ||
| 207 | virtual std::vector<VirtualFile> GetFiles() const = 0; | ||
| 208 | // Returns the file with filename matching name. Returns nullptr if directory doesn't have a | ||
| 209 | // file with name. | ||
| 210 | virtual VirtualFile GetFile(std::string_view name) const; | ||
| 211 | |||
| 212 | // Returns a struct containing the file's timestamp. | ||
| 213 | virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const; | ||
| 214 | |||
| 215 | // Returns a vector containing all of the subdirectories in this directory. | ||
| 216 | virtual std::vector<VirtualDir> GetSubdirectories() const = 0; | ||
| 217 | // Returns the directory with name matching name. Returns nullptr if directory doesn't have a | ||
| 218 | // directory with name. | ||
| 219 | virtual VirtualDir GetSubdirectory(std::string_view name) const; | ||
| 220 | |||
| 221 | // Returns whether or not the directory can be written to. | ||
| 222 | virtual bool IsWritable() const = 0; | ||
| 223 | // Returns whether of not the directory can be read from. | ||
| 224 | virtual bool IsReadable() const = 0; | ||
| 225 | |||
| 226 | // Returns whether or not the directory is the root of the current file tree. | ||
| 227 | virtual bool IsRoot() const; | ||
| 228 | |||
| 229 | // Returns the name of the directory. | ||
| 230 | virtual std::string GetName() const = 0; | ||
| 231 | // Returns the total size of all files and subdirectories in this directory. | ||
| 232 | virtual std::size_t GetSize() const; | ||
| 233 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or | ||
| 234 | // has no parent. | ||
| 235 | virtual VirtualDir GetParentDirectory() const = 0; | ||
| 236 | |||
| 237 | // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr | ||
| 238 | // if the operation failed. | ||
| 239 | virtual VirtualDir CreateSubdirectory(std::string_view name) = 0; | ||
| 240 | // Creates a new file with name name. Returns a pointer to the new file or nullptr if the | ||
| 241 | // operation failed. | ||
| 242 | virtual VirtualFile CreateFile(std::string_view name) = 0; | ||
| 243 | |||
| 244 | // Creates a new file at the path relative to this directory. Also creates directories if | ||
| 245 | // they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 246 | virtual VirtualFile CreateFileRelative(std::string_view path); | ||
| 247 | |||
| 248 | // Creates a new file at the path relative to root of this directory. Also creates directories | ||
| 249 | // if they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 250 | virtual VirtualFile CreateFileAbsolute(std::string_view path); | ||
| 251 | |||
| 252 | // Creates a new directory at the path relative to this directory. Also creates directories if | ||
| 253 | // they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 254 | virtual VirtualDir CreateDirectoryRelative(std::string_view path); | ||
| 255 | |||
| 256 | // Creates a new directory at the path relative to root of this directory. Also creates | ||
| 257 | // directories if they do not exist and is supported by this implementation. Returns nullptr on | ||
| 258 | // any failure. | ||
| 259 | virtual VirtualDir CreateDirectoryAbsolute(std::string_view path); | ||
| 260 | |||
| 261 | // Deletes the subdirectory with the given name and returns true on success. | ||
| 262 | virtual bool DeleteSubdirectory(std::string_view name) = 0; | ||
| 263 | |||
| 264 | // Deletes all subdirectories and files within the provided directory and then deletes | ||
| 265 | // the directory itself. Returns true on success. | ||
| 266 | virtual bool DeleteSubdirectoryRecursive(std::string_view name); | ||
| 267 | |||
| 268 | // Deletes all subdirectories and files within the provided directory. | ||
| 269 | // Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory. | ||
| 270 | virtual bool CleanSubdirectoryRecursive(std::string_view name); | ||
| 271 | |||
| 272 | // Returns whether or not the file with name name was deleted successfully. | ||
| 273 | virtual bool DeleteFile(std::string_view name) = 0; | ||
| 274 | |||
| 275 | // Returns whether or not this directory was renamed to name. | ||
| 276 | virtual bool Rename(std::string_view name) = 0; | ||
| 277 | |||
| 278 | // Returns whether or not the file with name src was successfully copied to a new file with name | ||
| 279 | // dest. | ||
| 280 | virtual bool Copy(std::string_view src, std::string_view dest); | ||
| 281 | |||
| 282 | // Gets all of the entries directly in the directory (files and dirs), returning a map between | ||
| 283 | // item name -> type. | ||
| 284 | virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; | ||
| 285 | |||
| 286 | // Returns the full path of this directory as a string, recursively | ||
| 287 | virtual std::string GetFullPath() const; | ||
| 288 | }; | ||
| 289 | |||
| 290 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work | ||
| 291 | // if writable. This is to avoid redundant empty methods everywhere. | ||
| 292 | class ReadOnlyVfsDirectory : public VfsDirectory { | ||
| 293 | public: | ||
| 294 | bool IsWritable() const override; | ||
| 295 | bool IsReadable() const override; | ||
| 296 | VirtualDir CreateSubdirectory(std::string_view name) override; | ||
| 297 | VirtualFile CreateFile(std::string_view name) override; | ||
| 298 | VirtualFile CreateFileAbsolute(std::string_view path) override; | ||
| 299 | VirtualFile CreateFileRelative(std::string_view path) override; | ||
| 300 | VirtualDir CreateDirectoryAbsolute(std::string_view path) override; | ||
| 301 | VirtualDir CreateDirectoryRelative(std::string_view path) override; | ||
| 302 | bool DeleteSubdirectory(std::string_view name) override; | ||
| 303 | bool DeleteSubdirectoryRecursive(std::string_view name) override; | ||
| 304 | bool CleanSubdirectoryRecursive(std::string_view name) override; | ||
| 305 | bool DeleteFile(std::string_view name) override; | ||
| 306 | bool Rename(std::string_view name) override; | ||
| 307 | }; | ||
| 308 | |||
| 309 | // Compare the two files, byte-for-byte, in increments specified by block_size | ||
| 310 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, | ||
| 311 | std::size_t block_size = 0x1000); | ||
| 312 | |||
| 313 | // A method that copies the raw data between two different implementations of VirtualFile. If you | ||
| 314 | // are using the same implementation, it is probably better to use the Copy method in the parent | ||
| 315 | // directory of src/dest. | ||
| 316 | bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000); | ||
| 317 | |||
| 318 | // A method that performs a similar function to VfsRawCopy above, but instead copies entire | ||
| 319 | // directories. It suffers the same performance penalties as above and an implementation-specific | ||
| 320 | // Copy should always be preferred. | ||
| 321 | bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000); | ||
| 322 | |||
| 323 | // Checks if the directory at path relative to rel exists. If it does, returns that. If it does not | ||
| 324 | // it attempts to create it and returns the new dir or nullptr on failure. | ||
| 325 | VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path); | ||
| 326 | |||
| 327 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_cached.cpp b/src/core/file_sys/vfs/vfs_cached.cpp new file mode 100644 index 000000000..01cd0f1e0 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_cached.cpp | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/file_sys/vfs/vfs_cached.h" | ||
| 5 | #include "core/file_sys/vfs/vfs_types.h" | ||
| 6 | |||
| 7 | namespace FileSys { | ||
| 8 | |||
| 9 | CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir) | ||
| 10 | : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) { | ||
| 11 | for (auto& dir : source_dir->GetSubdirectories()) { | ||
| 12 | dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir))); | ||
| 13 | } | ||
| 14 | for (auto& file : source_dir->GetFiles()) { | ||
| 15 | files.emplace(file->GetName(), std::move(file)); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | CachedVfsDirectory::~CachedVfsDirectory() = default; | ||
| 20 | |||
| 21 | VirtualFile CachedVfsDirectory::GetFile(std::string_view file_name) const { | ||
| 22 | auto it = files.find(file_name); | ||
| 23 | if (it != files.end()) { | ||
| 24 | return it->second; | ||
| 25 | } | ||
| 26 | |||
| 27 | return nullptr; | ||
| 28 | } | ||
| 29 | |||
| 30 | VirtualDir CachedVfsDirectory::GetSubdirectory(std::string_view dir_name) const { | ||
| 31 | auto it = dirs.find(dir_name); | ||
| 32 | if (it != dirs.end()) { | ||
| 33 | return it->second; | ||
| 34 | } | ||
| 35 | |||
| 36 | return nullptr; | ||
| 37 | } | ||
| 38 | |||
| 39 | std::vector<VirtualFile> CachedVfsDirectory::GetFiles() const { | ||
| 40 | std::vector<VirtualFile> out; | ||
| 41 | for (auto& [file_name, file] : files) { | ||
| 42 | out.push_back(file); | ||
| 43 | } | ||
| 44 | return out; | ||
| 45 | } | ||
| 46 | |||
| 47 | std::vector<VirtualDir> CachedVfsDirectory::GetSubdirectories() const { | ||
| 48 | std::vector<VirtualDir> out; | ||
| 49 | for (auto& [dir_name, dir] : dirs) { | ||
| 50 | out.push_back(dir); | ||
| 51 | } | ||
| 52 | return out; | ||
| 53 | } | ||
| 54 | |||
| 55 | std::string CachedVfsDirectory::GetName() const { | ||
| 56 | return name; | ||
| 57 | } | ||
| 58 | |||
| 59 | VirtualDir CachedVfsDirectory::GetParentDirectory() const { | ||
| 60 | return parent; | ||
| 61 | } | ||
| 62 | |||
| 63 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_cached.h b/src/core/file_sys/vfs/vfs_cached.h new file mode 100644 index 000000000..47dff7224 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_cached.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <string_view> | ||
| 7 | #include <vector> | ||
| 8 | #include "core/file_sys/vfs/vfs.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | class CachedVfsDirectory : public ReadOnlyVfsDirectory { | ||
| 13 | public: | ||
| 14 | CachedVfsDirectory(VirtualDir&& source_directory); | ||
| 15 | |||
| 16 | ~CachedVfsDirectory() override; | ||
| 17 | VirtualFile GetFile(std::string_view file_name) const override; | ||
| 18 | VirtualDir GetSubdirectory(std::string_view dir_name) const override; | ||
| 19 | std::vector<VirtualFile> GetFiles() const override; | ||
| 20 | std::vector<VirtualDir> GetSubdirectories() const override; | ||
| 21 | std::string GetName() const override; | ||
| 22 | VirtualDir GetParentDirectory() const override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | std::string name; | ||
| 26 | VirtualDir parent; | ||
| 27 | std::map<std::string, VirtualDir, std::less<>> dirs; | ||
| 28 | std::map<std::string, VirtualFile, std::less<>> files; | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_concat.cpp b/src/core/file_sys/vfs/vfs_concat.cpp new file mode 100644 index 000000000..b5cc9a9e9 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_concat.cpp | |||
| @@ -0,0 +1,192 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <utility> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "core/file_sys/vfs/vfs_concat.h" | ||
| 9 | #include "core/file_sys/vfs/vfs_static.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_) | ||
| 14 | : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) { | ||
| 15 | DEBUG_ASSERT(this->VerifyContinuity()); | ||
| 16 | } | ||
| 17 | |||
| 18 | bool ConcatenatedVfsFile::VerifyContinuity() const { | ||
| 19 | u64 last_offset = 0; | ||
| 20 | for (auto& entry : concatenation_map) { | ||
| 21 | if (entry.offset != last_offset) { | ||
| 22 | return false; | ||
| 23 | } | ||
| 24 | |||
| 25 | last_offset = entry.offset + entry.file->GetSize(); | ||
| 26 | } | ||
| 27 | |||
| 28 | return true; | ||
| 29 | } | ||
| 30 | |||
| 31 | ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; | ||
| 32 | |||
| 33 | VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name, | ||
| 34 | std::vector<VirtualFile>&& files) { | ||
| 35 | // Fold trivial cases. | ||
| 36 | if (files.empty()) { | ||
| 37 | return nullptr; | ||
| 38 | } | ||
| 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 | const auto size = file->GetSize(); | ||
| 50 | |||
| 51 | concatenation_map.emplace_back(ConcatenationEntry{ | ||
| 52 | .offset = last_offset, | ||
| 53 | .file = std::move(file), | ||
| 54 | }); | ||
| 55 | |||
| 56 | last_offset += size; | ||
| 57 | } | ||
| 58 | |||
| 59 | return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); | ||
| 60 | } | ||
| 61 | |||
| 62 | VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile( | ||
| 63 | u8 filler_byte, std::string&& name, std::vector<std::pair<u64, VirtualFile>>&& files) { | ||
| 64 | // Fold trivial cases. | ||
| 65 | if (files.empty()) { | ||
| 66 | return nullptr; | ||
| 67 | } | ||
| 68 | if (files.size() == 1) { | ||
| 69 | return files.begin()->second; | ||
| 70 | } | ||
| 71 | |||
| 72 | // Make the concatenation map from the input. | ||
| 73 | std::vector<ConcatenationEntry> concatenation_map; | ||
| 74 | |||
| 75 | concatenation_map.reserve(files.size()); | ||
| 76 | u64 last_offset = 0; | ||
| 77 | |||
| 78 | // Iteration of a multimap is ordered, so offset will be strictly non-decreasing. | ||
| 79 | for (auto& [offset, file] : files) { | ||
| 80 | const auto size = file->GetSize(); | ||
| 81 | |||
| 82 | if (offset > last_offset) { | ||
| 83 | concatenation_map.emplace_back(ConcatenationEntry{ | ||
| 84 | .offset = last_offset, | ||
| 85 | .file = std::make_shared<StaticVfsFile>(filler_byte, offset - last_offset), | ||
| 86 | }); | ||
| 87 | } | ||
| 88 | |||
| 89 | concatenation_map.emplace_back(ConcatenationEntry{ | ||
| 90 | .offset = offset, | ||
| 91 | .file = std::move(file), | ||
| 92 | }); | ||
| 93 | |||
| 94 | last_offset = offset + size; | ||
| 95 | } | ||
| 96 | |||
| 97 | return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string ConcatenatedVfsFile::GetName() const { | ||
| 101 | if (concatenation_map.empty()) { | ||
| 102 | return ""; | ||
| 103 | } | ||
| 104 | if (!name.empty()) { | ||
| 105 | return name; | ||
| 106 | } | ||
| 107 | return concatenation_map.front().file->GetName(); | ||
| 108 | } | ||
| 109 | |||
| 110 | std::size_t ConcatenatedVfsFile::GetSize() const { | ||
| 111 | if (concatenation_map.empty()) { | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | return concatenation_map.back().offset + concatenation_map.back().file->GetSize(); | ||
| 115 | } | ||
| 116 | |||
| 117 | bool ConcatenatedVfsFile::Resize(std::size_t new_size) { | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | |||
| 121 | VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const { | ||
| 122 | if (concatenation_map.empty()) { | ||
| 123 | return nullptr; | ||
| 124 | } | ||
| 125 | return concatenation_map.front().file->GetContainingDirectory(); | ||
| 126 | } | ||
| 127 | |||
| 128 | bool ConcatenatedVfsFile::IsWritable() const { | ||
| 129 | return false; | ||
| 130 | } | ||
| 131 | |||
| 132 | bool ConcatenatedVfsFile::IsReadable() const { | ||
| 133 | return true; | ||
| 134 | } | ||
| 135 | |||
| 136 | std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { | ||
| 137 | const ConcatenationEntry key{ | ||
| 138 | .offset = offset, | ||
| 139 | .file = nullptr, | ||
| 140 | }; | ||
| 141 | |||
| 142 | // Read nothing if the map is empty. | ||
| 143 | if (concatenation_map.empty()) { | ||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | // Binary search to find the iterator to the first position we can check. | ||
| 148 | // It must exist, since we are not empty and are comparing unsigned integers. | ||
| 149 | auto it = std::prev(std::upper_bound(concatenation_map.begin(), concatenation_map.end(), key)); | ||
| 150 | u64 cur_length = length; | ||
| 151 | u64 cur_offset = offset; | ||
| 152 | |||
| 153 | while (cur_length > 0 && it != concatenation_map.end()) { | ||
| 154 | // Check if we can read the file at this position. | ||
| 155 | const auto& file = it->file; | ||
| 156 | const u64 map_offset = it->offset; | ||
| 157 | const u64 file_size = file->GetSize(); | ||
| 158 | |||
| 159 | if (cur_offset > map_offset + file_size) { | ||
| 160 | // Entirely out of bounds read. | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | |||
| 164 | // Read the file at this position. | ||
| 165 | const u64 file_seek = cur_offset - map_offset; | ||
| 166 | const u64 intended_read_size = std::min<u64>(cur_length, file_size - file_seek); | ||
| 167 | const u64 actual_read_size = | ||
| 168 | file->Read(data + (cur_offset - offset), intended_read_size, file_seek); | ||
| 169 | |||
| 170 | // Update tracking. | ||
| 171 | cur_offset += actual_read_size; | ||
| 172 | cur_length -= actual_read_size; | ||
| 173 | it++; | ||
| 174 | |||
| 175 | // If we encountered a short read, we're done. | ||
| 176 | if (actual_read_size < intended_read_size) { | ||
| 177 | break; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | return cur_offset - offset; | ||
| 182 | } | ||
| 183 | |||
| 184 | std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { | ||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | bool ConcatenatedVfsFile::Rename(std::string_view new_name) { | ||
| 189 | return false; | ||
| 190 | } | ||
| 191 | |||
| 192 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_concat.h b/src/core/file_sys/vfs/vfs_concat.h new file mode 100644 index 000000000..6d12af762 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_concat.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <compare> | ||
| 7 | #include <map> | ||
| 8 | #include <memory> | ||
| 9 | #include "core/file_sys/vfs/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | // Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently | ||
| 14 | // read-only. | ||
| 15 | class ConcatenatedVfsFile : public VfsFile { | ||
| 16 | private: | ||
| 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::string&& name, | ||
| 28 | std::vector<ConcatenationEntry>&& concatenation_map); | ||
| 29 | bool VerifyContinuity() const; | ||
| 30 | |||
| 31 | public: | ||
| 32 | ~ConcatenatedVfsFile() override; | ||
| 33 | |||
| 34 | /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. | ||
| 35 | static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files); | ||
| 36 | |||
| 37 | /// Convenience function that turns a map of offsets to files into a concatenated file, filling | ||
| 38 | /// gaps with a given filler byte. | ||
| 39 | static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name, | ||
| 40 | std::vector<std::pair<u64, VirtualFile>>&& files); | ||
| 41 | |||
| 42 | std::string GetName() const override; | ||
| 43 | std::size_t GetSize() const override; | ||
| 44 | bool Resize(std::size_t new_size) override; | ||
| 45 | VirtualDir GetContainingDirectory() const override; | ||
| 46 | bool IsWritable() const override; | ||
| 47 | bool IsReadable() const override; | ||
| 48 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | ||
| 49 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; | ||
| 50 | bool Rename(std::string_view new_name) override; | ||
| 51 | |||
| 52 | private: | ||
| 53 | ConcatenationMap concatenation_map; | ||
| 54 | std::string name; | ||
| 55 | }; | ||
| 56 | |||
| 57 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_layered.cpp b/src/core/file_sys/vfs/vfs_layered.cpp new file mode 100644 index 000000000..47b2a3c78 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_layered.cpp | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <set> | ||
| 6 | #include <unordered_set> | ||
| 7 | #include <utility> | ||
| 8 | #include "core/file_sys/vfs/vfs_layered.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_) | ||
| 13 | : dirs(std::move(dirs_)), name(std::move(name_)) {} | ||
| 14 | |||
| 15 | LayeredVfsDirectory::~LayeredVfsDirectory() = default; | ||
| 16 | |||
| 17 | VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs, | ||
| 18 | std::string name) { | ||
| 19 | if (dirs.empty()) | ||
| 20 | return nullptr; | ||
| 21 | if (dirs.size() == 1) | ||
| 22 | return dirs[0]; | ||
| 23 | |||
| 24 | return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name))); | ||
| 25 | } | ||
| 26 | |||
| 27 | VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const { | ||
| 28 | for (const auto& layer : dirs) { | ||
| 29 | const auto file = layer->GetFileRelative(path); | ||
| 30 | if (file != nullptr) | ||
| 31 | return file; | ||
| 32 | } | ||
| 33 | |||
| 34 | return nullptr; | ||
| 35 | } | ||
| 36 | |||
| 37 | VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) const { | ||
| 38 | std::vector<VirtualDir> out; | ||
| 39 | for (const auto& layer : dirs) { | ||
| 40 | auto dir = layer->GetDirectoryRelative(path); | ||
| 41 | if (dir != nullptr) { | ||
| 42 | out.emplace_back(std::move(dir)); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | return MakeLayeredDirectory(std::move(out)); | ||
| 47 | } | ||
| 48 | |||
| 49 | VirtualFile LayeredVfsDirectory::GetFile(std::string_view file_name) const { | ||
| 50 | return GetFileRelative(file_name); | ||
| 51 | } | ||
| 52 | |||
| 53 | VirtualDir LayeredVfsDirectory::GetSubdirectory(std::string_view subdir_name) const { | ||
| 54 | return GetDirectoryRelative(subdir_name); | ||
| 55 | } | ||
| 56 | |||
| 57 | std::string LayeredVfsDirectory::GetFullPath() const { | ||
| 58 | return dirs[0]->GetFullPath(); | ||
| 59 | } | ||
| 60 | |||
| 61 | std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const { | ||
| 62 | std::vector<VirtualFile> out; | ||
| 63 | std::unordered_set<std::string> out_names; | ||
| 64 | |||
| 65 | for (const auto& layer : dirs) { | ||
| 66 | for (auto& file : layer->GetFiles()) { | ||
| 67 | const auto [it, is_new] = out_names.emplace(file->GetName()); | ||
| 68 | if (is_new) { | ||
| 69 | out.emplace_back(std::move(file)); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | return out; | ||
| 75 | } | ||
| 76 | |||
| 77 | std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const { | ||
| 78 | std::vector<VirtualDir> out; | ||
| 79 | std::unordered_set<std::string> out_names; | ||
| 80 | |||
| 81 | for (const auto& layer : dirs) { | ||
| 82 | for (const auto& sd : layer->GetSubdirectories()) { | ||
| 83 | out_names.emplace(sd->GetName()); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | out.reserve(out_names.size()); | ||
| 88 | for (const auto& subdir : out_names) { | ||
| 89 | out.emplace_back(GetSubdirectory(subdir)); | ||
| 90 | } | ||
| 91 | |||
| 92 | return out; | ||
| 93 | } | ||
| 94 | |||
| 95 | bool LayeredVfsDirectory::IsWritable() const { | ||
| 96 | return false; | ||
| 97 | } | ||
| 98 | |||
| 99 | bool LayeredVfsDirectory::IsReadable() const { | ||
| 100 | return true; | ||
| 101 | } | ||
| 102 | |||
| 103 | std::string LayeredVfsDirectory::GetName() const { | ||
| 104 | return name.empty() ? dirs[0]->GetName() : name; | ||
| 105 | } | ||
| 106 | |||
| 107 | VirtualDir LayeredVfsDirectory::GetParentDirectory() const { | ||
| 108 | return dirs[0]->GetParentDirectory(); | ||
| 109 | } | ||
| 110 | |||
| 111 | VirtualDir LayeredVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { | ||
| 112 | return nullptr; | ||
| 113 | } | ||
| 114 | |||
| 115 | VirtualFile LayeredVfsDirectory::CreateFile(std::string_view file_name) { | ||
| 116 | return nullptr; | ||
| 117 | } | ||
| 118 | |||
| 119 | bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | |||
| 123 | bool LayeredVfsDirectory::DeleteFile(std::string_view file_name) { | ||
| 124 | return false; | ||
| 125 | } | ||
| 126 | |||
| 127 | bool LayeredVfsDirectory::Rename(std::string_view new_name) { | ||
| 128 | name = new_name; | ||
| 129 | return true; | ||
| 130 | } | ||
| 131 | |||
| 132 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_layered.h b/src/core/file_sys/vfs/vfs_layered.h new file mode 100644 index 000000000..0027ffa9a --- /dev/null +++ b/src/core/file_sys/vfs/vfs_layered.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include "core/file_sys/vfs/vfs.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | // Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first | ||
| 12 | // one and falling back to the one after. The highest priority directory (overwrites all others) | ||
| 13 | // should be element 0 in the dirs vector. | ||
| 14 | class LayeredVfsDirectory : public VfsDirectory { | ||
| 15 | explicit LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_); | ||
| 16 | |||
| 17 | public: | ||
| 18 | ~LayeredVfsDirectory() override; | ||
| 19 | |||
| 20 | /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. | ||
| 21 | static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = ""); | ||
| 22 | |||
| 23 | VirtualFile GetFileRelative(std::string_view path) const override; | ||
| 24 | VirtualDir GetDirectoryRelative(std::string_view path) const override; | ||
| 25 | VirtualFile GetFile(std::string_view file_name) const override; | ||
| 26 | VirtualDir GetSubdirectory(std::string_view subdir_name) const override; | ||
| 27 | std::string GetFullPath() const override; | ||
| 28 | |||
| 29 | std::vector<VirtualFile> GetFiles() const override; | ||
| 30 | std::vector<VirtualDir> GetSubdirectories() const override; | ||
| 31 | bool IsWritable() const override; | ||
| 32 | bool IsReadable() const override; | ||
| 33 | std::string GetName() const override; | ||
| 34 | VirtualDir GetParentDirectory() const override; | ||
| 35 | VirtualDir CreateSubdirectory(std::string_view subdir_name) override; | ||
| 36 | VirtualFile CreateFile(std::string_view file_name) override; | ||
| 37 | bool DeleteSubdirectory(std::string_view subdir_name) override; | ||
| 38 | bool DeleteFile(std::string_view file_name) override; | ||
| 39 | bool Rename(std::string_view new_name) override; | ||
| 40 | |||
| 41 | private: | ||
| 42 | std::vector<VirtualDir> dirs; | ||
| 43 | std::string name; | ||
| 44 | }; | ||
| 45 | |||
| 46 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_offset.cpp b/src/core/file_sys/vfs/vfs_offset.cpp new file mode 100644 index 000000000..1a37d2670 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_offset.cpp | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <utility> | ||
| 6 | |||
| 7 | #include "core/file_sys/vfs/vfs_offset.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | OffsetVfsFile::OffsetVfsFile(VirtualFile file_, std::size_t size_, std::size_t offset_, | ||
| 12 | std::string name_, VirtualDir parent_) | ||
| 13 | : file(file_), offset(offset_), size(size_), name(std::move(name_)), | ||
| 14 | parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} | ||
| 15 | |||
| 16 | OffsetVfsFile::~OffsetVfsFile() = default; | ||
| 17 | |||
| 18 | std::string OffsetVfsFile::GetName() const { | ||
| 19 | return name.empty() ? file->GetName() : name; | ||
| 20 | } | ||
| 21 | |||
| 22 | std::size_t OffsetVfsFile::GetSize() const { | ||
| 23 | return size; | ||
| 24 | } | ||
| 25 | |||
| 26 | bool OffsetVfsFile::Resize(std::size_t new_size) { | ||
| 27 | if (offset + new_size < file->GetSize()) { | ||
| 28 | size = new_size; | ||
| 29 | } else { | ||
| 30 | auto res = file->Resize(offset + new_size); | ||
| 31 | if (!res) | ||
| 32 | return false; | ||
| 33 | size = new_size; | ||
| 34 | } | ||
| 35 | |||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 39 | VirtualDir OffsetVfsFile::GetContainingDirectory() const { | ||
| 40 | return parent; | ||
| 41 | } | ||
| 42 | |||
| 43 | bool OffsetVfsFile::IsWritable() const { | ||
| 44 | return file->IsWritable(); | ||
| 45 | } | ||
| 46 | |||
| 47 | bool OffsetVfsFile::IsReadable() const { | ||
| 48 | return file->IsReadable(); | ||
| 49 | } | ||
| 50 | |||
| 51 | std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const { | ||
| 52 | return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); | ||
| 53 | } | ||
| 54 | |||
| 55 | std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) { | ||
| 56 | return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); | ||
| 57 | } | ||
| 58 | |||
| 59 | std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { | ||
| 60 | if (r_offset >= size) { | ||
| 61 | return std::nullopt; | ||
| 62 | } | ||
| 63 | |||
| 64 | return file->ReadByte(offset + r_offset); | ||
| 65 | } | ||
| 66 | |||
| 67 | std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { | ||
| 68 | return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); | ||
| 69 | } | ||
| 70 | |||
| 71 | std::vector<u8> OffsetVfsFile::ReadAllBytes() const { | ||
| 72 | return file->ReadBytes(size, offset); | ||
| 73 | } | ||
| 74 | |||
| 75 | bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) { | ||
| 76 | if (r_offset < size) | ||
| 77 | return file->WriteByte(data, offset + r_offset); | ||
| 78 | |||
| 79 | return false; | ||
| 80 | } | ||
| 81 | |||
| 82 | std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) { | ||
| 83 | return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); | ||
| 84 | } | ||
| 85 | |||
| 86 | bool OffsetVfsFile::Rename(std::string_view new_name) { | ||
| 87 | return file->Rename(new_name); | ||
| 88 | } | ||
| 89 | |||
| 90 | std::size_t OffsetVfsFile::GetOffset() const { | ||
| 91 | return offset; | ||
| 92 | } | ||
| 93 | |||
| 94 | std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const { | ||
| 95 | return std::clamp(r_size, std::size_t{0}, size - r_offset); | ||
| 96 | } | ||
| 97 | |||
| 98 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_offset.h b/src/core/file_sys/vfs/vfs_offset.h new file mode 100644 index 000000000..4abe41d8e --- /dev/null +++ b/src/core/file_sys/vfs/vfs_offset.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "core/file_sys/vfs/vfs.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | // An implementation of VfsFile that wraps around another VfsFile at a certain offset. | ||
| 13 | // Similar to seeking to an offset. | ||
| 14 | // If the file is writable, operations that would write past the end of the offset file will expand | ||
| 15 | // the size of this wrapper. | ||
| 16 | class OffsetVfsFile : public VfsFile { | ||
| 17 | public: | ||
| 18 | OffsetVfsFile(VirtualFile file, std::size_t size, std::size_t offset = 0, | ||
| 19 | std::string new_name = "", VirtualDir new_parent = nullptr); | ||
| 20 | ~OffsetVfsFile() override; | ||
| 21 | |||
| 22 | std::string GetName() const override; | ||
| 23 | std::size_t GetSize() const override; | ||
| 24 | bool Resize(std::size_t new_size) override; | ||
| 25 | VirtualDir GetContainingDirectory() const override; | ||
| 26 | bool IsWritable() const override; | ||
| 27 | bool IsReadable() const override; | ||
| 28 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | ||
| 29 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; | ||
| 30 | std::optional<u8> ReadByte(std::size_t offset) const override; | ||
| 31 | std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; | ||
| 32 | std::vector<u8> ReadAllBytes() const override; | ||
| 33 | bool WriteByte(u8 data, std::size_t offset) override; | ||
| 34 | std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override; | ||
| 35 | |||
| 36 | bool Rename(std::string_view new_name) override; | ||
| 37 | |||
| 38 | std::size_t GetOffset() const; | ||
| 39 | |||
| 40 | private: | ||
| 41 | std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const; | ||
| 42 | |||
| 43 | VirtualFile file; | ||
| 44 | std::size_t offset; | ||
| 45 | std::size_t size; | ||
| 46 | std::string name; | ||
| 47 | VirtualDir parent; | ||
| 48 | }; | ||
| 49 | |||
| 50 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp new file mode 100644 index 000000000..1e6d8163b --- /dev/null +++ b/src/core/file_sys/vfs/vfs_real.cpp | |||
| @@ -0,0 +1,528 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <cstddef> | ||
| 6 | #include <iterator> | ||
| 7 | #include <utility> | ||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/fs/file.h" | ||
| 10 | #include "common/fs/fs.h" | ||
| 11 | #include "common/fs/path_util.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "core/file_sys/vfs/vfs.h" | ||
| 14 | #include "core/file_sys/vfs/vfs_real.h" | ||
| 15 | |||
| 16 | // For FileTimeStampRaw | ||
| 17 | #include <sys/stat.h> | ||
| 18 | |||
| 19 | #ifdef _MSC_VER | ||
| 20 | #define stat _stat64 | ||
| 21 | #endif | ||
| 22 | |||
| 23 | namespace FileSys { | ||
| 24 | |||
| 25 | namespace FS = Common::FS; | ||
| 26 | |||
| 27 | namespace { | ||
| 28 | |||
| 29 | constexpr size_t MaxOpenFiles = 512; | ||
| 30 | |||
| 31 | constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { | ||
| 32 | switch (mode) { | ||
| 33 | case Mode::Read: | ||
| 34 | return FS::FileAccessMode::Read; | ||
| 35 | case Mode::Write: | ||
| 36 | case Mode::ReadWrite: | ||
| 37 | case Mode::Append: | ||
| 38 | case Mode::ReadAppend: | ||
| 39 | case Mode::WriteAppend: | ||
| 40 | case Mode::All: | ||
| 41 | return FS::FileAccessMode::ReadWrite; | ||
| 42 | default: | ||
| 43 | return {}; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | } // Anonymous namespace | ||
| 48 | |||
| 49 | RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} | ||
| 50 | RealVfsFilesystem::~RealVfsFilesystem() = default; | ||
| 51 | |||
| 52 | std::string RealVfsFilesystem::GetName() const { | ||
| 53 | return "Real"; | ||
| 54 | } | ||
| 55 | |||
| 56 | bool RealVfsFilesystem::IsReadable() const { | ||
| 57 | return true; | ||
| 58 | } | ||
| 59 | |||
| 60 | bool RealVfsFilesystem::IsWritable() const { | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { | ||
| 65 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | ||
| 66 | if (!FS::Exists(path)) { | ||
| 67 | return VfsEntryType::None; | ||
| 68 | } | ||
| 69 | if (FS::IsDir(path)) { | ||
| 70 | return VfsEntryType::Directory; | ||
| 71 | } | ||
| 72 | |||
| 73 | return VfsEntryType::File; | ||
| 74 | } | ||
| 75 | |||
| 76 | VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size, | ||
| 77 | Mode perms) { | ||
| 78 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | ||
| 79 | std::scoped_lock lk{list_lock}; | ||
| 80 | |||
| 81 | if (auto it = cache.find(path); it != cache.end()) { | ||
| 82 | if (auto file = it->second.lock(); file) { | ||
| 83 | return file; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | if (!size && !FS::IsFile(path)) { | ||
| 88 | return nullptr; | ||
| 89 | } | ||
| 90 | |||
| 91 | auto reference = std::make_unique<FileReference>(); | ||
| 92 | this->InsertReferenceIntoListLocked(*reference); | ||
| 93 | |||
| 94 | auto file = std::shared_ptr<RealVfsFile>( | ||
| 95 | new RealVfsFile(*this, std::move(reference), path, perms, size)); | ||
| 96 | cache[path] = file; | ||
| 97 | |||
| 98 | return file; | ||
| 99 | } | ||
| 100 | |||
| 101 | VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | ||
| 102 | return OpenFileFromEntry(path_, {}, perms); | ||
| 103 | } | ||
| 104 | |||
| 105 | VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { | ||
| 106 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | ||
| 107 | { | ||
| 108 | std::scoped_lock lk{list_lock}; | ||
| 109 | cache.erase(path); | ||
| 110 | } | ||
| 111 | |||
| 112 | // Current usages of CreateFile expect to delete the contents of an existing file. | ||
| 113 | if (FS::IsFile(path)) { | ||
| 114 | FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; | ||
| 115 | |||
| 116 | if (!temp.IsOpen()) { | ||
| 117 | return nullptr; | ||
| 118 | } | ||
| 119 | |||
| 120 | temp.Close(); | ||
| 121 | |||
| 122 | return OpenFile(path, perms); | ||
| 123 | } | ||
| 124 | |||
| 125 | if (!FS::NewFile(path)) { | ||
| 126 | return nullptr; | ||
| 127 | } | ||
| 128 | |||
| 129 | return OpenFile(path, perms); | ||
| 130 | } | ||
| 131 | |||
| 132 | VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { | ||
| 133 | // Unused | ||
| 134 | return nullptr; | ||
| 135 | } | ||
| 136 | |||
| 137 | VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { | ||
| 138 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); | ||
| 139 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); | ||
| 140 | { | ||
| 141 | std::scoped_lock lk{list_lock}; | ||
| 142 | cache.erase(old_path); | ||
| 143 | cache.erase(new_path); | ||
| 144 | } | ||
| 145 | if (!FS::RenameFile(old_path, new_path)) { | ||
| 146 | return nullptr; | ||
| 147 | } | ||
| 148 | return OpenFile(new_path, Mode::ReadWrite); | ||
| 149 | } | ||
| 150 | |||
| 151 | bool RealVfsFilesystem::DeleteFile(std::string_view path_) { | ||
| 152 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | ||
| 153 | { | ||
| 154 | std::scoped_lock lk{list_lock}; | ||
| 155 | cache.erase(path); | ||
| 156 | } | ||
| 157 | return FS::RemoveFile(path); | ||
| 158 | } | ||
| 159 | |||
| 160 | VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { | ||
| 161 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | ||
| 162 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); | ||
| 163 | } | ||
| 164 | |||
| 165 | VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { | ||
| 166 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | ||
| 167 | if (!FS::CreateDirs(path)) { | ||
| 168 | return nullptr; | ||
| 169 | } | ||
| 170 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); | ||
| 171 | } | ||
| 172 | |||
| 173 | VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, | ||
| 174 | std::string_view new_path_) { | ||
| 175 | // Unused | ||
| 176 | return nullptr; | ||
| 177 | } | ||
| 178 | |||
| 179 | VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, | ||
| 180 | std::string_view new_path_) { | ||
| 181 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); | ||
| 182 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); | ||
| 183 | |||
| 184 | if (!FS::RenameDir(old_path, new_path)) { | ||
| 185 | return nullptr; | ||
| 186 | } | ||
| 187 | return OpenDirectory(new_path, Mode::ReadWrite); | ||
| 188 | } | ||
| 189 | |||
| 190 | bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { | ||
| 191 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | ||
| 192 | return FS::RemoveDirRecursively(path); | ||
| 193 | } | ||
| 194 | |||
| 195 | std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path, | ||
| 196 | Mode perms, | ||
| 197 | FileReference& reference) { | ||
| 198 | std::unique_lock lk{list_lock}; | ||
| 199 | |||
| 200 | // Temporarily remove from list. | ||
| 201 | this->RemoveReferenceFromListLocked(reference); | ||
| 202 | |||
| 203 | // Restore file if needed. | ||
| 204 | if (!reference.file) { | ||
| 205 | this->EvictSingleReferenceLocked(); | ||
| 206 | |||
| 207 | reference.file = | ||
| 208 | FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); | ||
| 209 | if (reference.file) { | ||
| 210 | num_open_files++; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | // Reinsert into list. | ||
| 215 | this->InsertReferenceIntoListLocked(reference); | ||
| 216 | |||
| 217 | return lk; | ||
| 218 | } | ||
| 219 | |||
| 220 | void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) { | ||
| 221 | std::scoped_lock lk{list_lock}; | ||
| 222 | |||
| 223 | // Remove from list. | ||
| 224 | this->RemoveReferenceFromListLocked(*reference); | ||
| 225 | |||
| 226 | // Close the file. | ||
| 227 | if (reference->file) { | ||
| 228 | reference->file.reset(); | ||
| 229 | num_open_files--; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | void RealVfsFilesystem::EvictSingleReferenceLocked() { | ||
| 234 | if (num_open_files < MaxOpenFiles || open_references.empty()) { | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | |||
| 238 | // Get and remove from list. | ||
| 239 | auto& reference = open_references.back(); | ||
| 240 | this->RemoveReferenceFromListLocked(reference); | ||
| 241 | |||
| 242 | // Close the file. | ||
| 243 | if (reference.file) { | ||
| 244 | reference.file.reset(); | ||
| 245 | num_open_files--; | ||
| 246 | } | ||
| 247 | |||
| 248 | // Reinsert into closed list. | ||
| 249 | this->InsertReferenceIntoListLocked(reference); | ||
| 250 | } | ||
| 251 | |||
| 252 | void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) { | ||
| 253 | if (reference.file) { | ||
| 254 | open_references.push_front(reference); | ||
| 255 | } else { | ||
| 256 | closed_references.push_front(reference); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) { | ||
| 261 | if (reference.file) { | ||
| 262 | open_references.erase(open_references.iterator_to(reference)); | ||
| 263 | } else { | ||
| 264 | closed_references.erase(closed_references.iterator_to(reference)); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, | ||
| 269 | const std::string& path_, Mode perms_, std::optional<u64> size_) | ||
| 270 | : base(base_), reference(std::move(reference_)), path(path_), | ||
| 271 | parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)), | ||
| 272 | size(size_), perms(perms_) {} | ||
| 273 | |||
| 274 | RealVfsFile::~RealVfsFile() { | ||
| 275 | base.DropReference(std::move(reference)); | ||
| 276 | } | ||
| 277 | |||
| 278 | std::string RealVfsFile::GetName() const { | ||
| 279 | return path_components.empty() ? "" : std::string(path_components.back()); | ||
| 280 | } | ||
| 281 | |||
| 282 | std::size_t RealVfsFile::GetSize() const { | ||
| 283 | if (size) { | ||
| 284 | return *size; | ||
| 285 | } | ||
| 286 | auto lk = base.RefreshReference(path, perms, *reference); | ||
| 287 | return reference->file ? reference->file->GetSize() : 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | bool RealVfsFile::Resize(std::size_t new_size) { | ||
| 291 | size.reset(); | ||
| 292 | auto lk = base.RefreshReference(path, perms, *reference); | ||
| 293 | return reference->file ? reference->file->SetSize(new_size) : false; | ||
| 294 | } | ||
| 295 | |||
| 296 | VirtualDir RealVfsFile::GetContainingDirectory() const { | ||
| 297 | return base.OpenDirectory(parent_path, perms); | ||
| 298 | } | ||
| 299 | |||
| 300 | bool RealVfsFile::IsWritable() const { | ||
| 301 | return True(perms & Mode::Write); | ||
| 302 | } | ||
| 303 | |||
| 304 | bool RealVfsFile::IsReadable() const { | ||
| 305 | return True(perms & Mode::Read); | ||
| 306 | } | ||
| 307 | |||
| 308 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { | ||
| 309 | auto lk = base.RefreshReference(path, perms, *reference); | ||
| 310 | if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { | ||
| 311 | return 0; | ||
| 312 | } | ||
| 313 | return reference->file->ReadSpan(std::span{data, length}); | ||
| 314 | } | ||
| 315 | |||
| 316 | std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { | ||
| 317 | size.reset(); | ||
| 318 | auto lk = base.RefreshReference(path, perms, *reference); | ||
| 319 | if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) { | ||
| 320 | return 0; | ||
| 321 | } | ||
| 322 | return reference->file->WriteSpan(std::span{data, length}); | ||
| 323 | } | ||
| 324 | |||
| 325 | bool RealVfsFile::Rename(std::string_view name) { | ||
| 326 | return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; | ||
| 327 | } | ||
| 328 | |||
| 329 | // TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if | ||
| 330 | // constexpr' because there is a compile error in the branch not used. | ||
| 331 | |||
| 332 | template <> | ||
| 333 | std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { | ||
| 334 | if (perms == Mode::Append) { | ||
| 335 | return {}; | ||
| 336 | } | ||
| 337 | |||
| 338 | std::vector<VirtualFile> out; | ||
| 339 | |||
| 340 | const FS::DirEntryCallable callback = [this, | ||
| 341 | &out](const std::filesystem::directory_entry& entry) { | ||
| 342 | const auto full_path_string = FS::PathToUTF8String(entry.path()); | ||
| 343 | |||
| 344 | out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms)); | ||
| 345 | |||
| 346 | return true; | ||
| 347 | }; | ||
| 348 | |||
| 349 | FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File); | ||
| 350 | |||
| 351 | return out; | ||
| 352 | } | ||
| 353 | |||
| 354 | template <> | ||
| 355 | std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { | ||
| 356 | if (perms == Mode::Append) { | ||
| 357 | return {}; | ||
| 358 | } | ||
| 359 | |||
| 360 | std::vector<VirtualDir> out; | ||
| 361 | |||
| 362 | const FS::DirEntryCallable callback = [this, | ||
| 363 | &out](const std::filesystem::directory_entry& entry) { | ||
| 364 | const auto full_path_string = FS::PathToUTF8String(entry.path()); | ||
| 365 | |||
| 366 | out.emplace_back(base.OpenDirectory(full_path_string, perms)); | ||
| 367 | |||
| 368 | return true; | ||
| 369 | }; | ||
| 370 | |||
| 371 | FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory); | ||
| 372 | |||
| 373 | return out; | ||
| 374 | } | ||
| 375 | |||
| 376 | RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) | ||
| 377 | : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), | ||
| 378 | path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) { | ||
| 379 | if (!FS::Exists(path) && True(perms & Mode::Write)) { | ||
| 380 | void(FS::CreateDirs(path)); | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | RealVfsDirectory::~RealVfsDirectory() = default; | ||
| 385 | |||
| 386 | VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { | ||
| 387 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); | ||
| 388 | if (!FS::Exists(full_path) || FS::IsDir(full_path)) { | ||
| 389 | return nullptr; | ||
| 390 | } | ||
| 391 | return base.OpenFile(full_path, perms); | ||
| 392 | } | ||
| 393 | |||
| 394 | VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { | ||
| 395 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); | ||
| 396 | if (!FS::Exists(full_path) || !FS::IsDir(full_path)) { | ||
| 397 | return nullptr; | ||
| 398 | } | ||
| 399 | return base.OpenDirectory(full_path, perms); | ||
| 400 | } | ||
| 401 | |||
| 402 | VirtualFile RealVfsDirectory::GetFile(std::string_view name) const { | ||
| 403 | return GetFileRelative(name); | ||
| 404 | } | ||
| 405 | |||
| 406 | VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const { | ||
| 407 | return GetDirectoryRelative(name); | ||
| 408 | } | ||
| 409 | |||
| 410 | VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) { | ||
| 411 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); | ||
| 412 | if (!FS::CreateParentDirs(full_path)) { | ||
| 413 | return nullptr; | ||
| 414 | } | ||
| 415 | return base.CreateFile(full_path, perms); | ||
| 416 | } | ||
| 417 | |||
| 418 | VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) { | ||
| 419 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); | ||
| 420 | return base.CreateDirectory(full_path, perms); | ||
| 421 | } | ||
| 422 | |||
| 423 | bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { | ||
| 424 | const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name)); | ||
| 425 | return base.DeleteDirectory(full_path); | ||
| 426 | } | ||
| 427 | |||
| 428 | std::vector<VirtualFile> RealVfsDirectory::GetFiles() const { | ||
| 429 | return IterateEntries<RealVfsFile, VfsFile>(); | ||
| 430 | } | ||
| 431 | |||
| 432 | FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const { | ||
| 433 | const auto full_path = FS::SanitizePath(path + '/' + std::string(path_)); | ||
| 434 | const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)}; | ||
| 435 | struct stat file_status; | ||
| 436 | |||
| 437 | #ifdef _WIN32 | ||
| 438 | const auto stat_result = _wstat64(fs_path.c_str(), &file_status); | ||
| 439 | #else | ||
| 440 | const auto stat_result = stat(fs_path.c_str(), &file_status); | ||
| 441 | #endif | ||
| 442 | |||
| 443 | if (stat_result != 0) { | ||
| 444 | return {}; | ||
| 445 | } | ||
| 446 | |||
| 447 | return { | ||
| 448 | .created{static_cast<u64>(file_status.st_ctime)}, | ||
| 449 | .accessed{static_cast<u64>(file_status.st_atime)}, | ||
| 450 | .modified{static_cast<u64>(file_status.st_mtime)}, | ||
| 451 | }; | ||
| 452 | } | ||
| 453 | |||
| 454 | std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const { | ||
| 455 | return IterateEntries<RealVfsDirectory, VfsDirectory>(); | ||
| 456 | } | ||
| 457 | |||
| 458 | bool RealVfsDirectory::IsWritable() const { | ||
| 459 | return True(perms & Mode::Write); | ||
| 460 | } | ||
| 461 | |||
| 462 | bool RealVfsDirectory::IsReadable() const { | ||
| 463 | return True(perms & Mode::Read); | ||
| 464 | } | ||
| 465 | |||
| 466 | std::string RealVfsDirectory::GetName() const { | ||
| 467 | return path_components.empty() ? "" : std::string(path_components.back()); | ||
| 468 | } | ||
| 469 | |||
| 470 | VirtualDir RealVfsDirectory::GetParentDirectory() const { | ||
| 471 | if (path_components.size() <= 1) { | ||
| 472 | return nullptr; | ||
| 473 | } | ||
| 474 | |||
| 475 | return base.OpenDirectory(parent_path, perms); | ||
| 476 | } | ||
| 477 | |||
| 478 | VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) { | ||
| 479 | const std::string subdir_path = (path + '/').append(name); | ||
| 480 | return base.CreateDirectory(subdir_path, perms); | ||
| 481 | } | ||
| 482 | |||
| 483 | VirtualFile RealVfsDirectory::CreateFile(std::string_view name) { | ||
| 484 | const std::string file_path = (path + '/').append(name); | ||
| 485 | return base.CreateFile(file_path, perms); | ||
| 486 | } | ||
| 487 | |||
| 488 | bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { | ||
| 489 | const std::string subdir_path = (path + '/').append(name); | ||
| 490 | return base.DeleteDirectory(subdir_path); | ||
| 491 | } | ||
| 492 | |||
| 493 | bool RealVfsDirectory::DeleteFile(std::string_view name) { | ||
| 494 | const std::string file_path = (path + '/').append(name); | ||
| 495 | return base.DeleteFile(file_path); | ||
| 496 | } | ||
| 497 | |||
| 498 | bool RealVfsDirectory::Rename(std::string_view name) { | ||
| 499 | const std::string new_name = (parent_path + '/').append(name); | ||
| 500 | return base.MoveFile(path, new_name) != nullptr; | ||
| 501 | } | ||
| 502 | |||
| 503 | std::string RealVfsDirectory::GetFullPath() const { | ||
| 504 | auto out = path; | ||
| 505 | std::replace(out.begin(), out.end(), '\\', '/'); | ||
| 506 | return out; | ||
| 507 | } | ||
| 508 | |||
| 509 | std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { | ||
| 510 | if (perms == Mode::Append) { | ||
| 511 | return {}; | ||
| 512 | } | ||
| 513 | |||
| 514 | std::map<std::string, VfsEntryType, std::less<>> out; | ||
| 515 | |||
| 516 | const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { | ||
| 517 | const auto filename = FS::PathToUTF8String(entry.path().filename()); | ||
| 518 | out.insert_or_assign(filename, | ||
| 519 | entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File); | ||
| 520 | return true; | ||
| 521 | }; | ||
| 522 | |||
| 523 | FS::IterateDirEntries(path, callback); | ||
| 524 | |||
| 525 | return out; | ||
| 526 | } | ||
| 527 | |||
| 528 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h new file mode 100644 index 000000000..1560bc1f9 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_real.h | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <map> | ||
| 7 | #include <mutex> | ||
| 8 | #include <optional> | ||
| 9 | #include <string_view> | ||
| 10 | #include "common/intrusive_list.h" | ||
| 11 | #include "core/file_sys/mode.h" | ||
| 12 | #include "core/file_sys/vfs/vfs.h" | ||
| 13 | |||
| 14 | namespace Common::FS { | ||
| 15 | class IOFile; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace FileSys { | ||
| 19 | |||
| 20 | struct FileReference : public Common::IntrusiveListBaseNode<FileReference> { | ||
| 21 | std::shared_ptr<Common::FS::IOFile> file{}; | ||
| 22 | }; | ||
| 23 | |||
| 24 | class RealVfsFile; | ||
| 25 | class RealVfsDirectory; | ||
| 26 | |||
| 27 | class RealVfsFilesystem : public VfsFilesystem { | ||
| 28 | public: | ||
| 29 | RealVfsFilesystem(); | ||
| 30 | ~RealVfsFilesystem() override; | ||
| 31 | |||
| 32 | std::string GetName() const override; | ||
| 33 | bool IsReadable() const override; | ||
| 34 | bool IsWritable() const override; | ||
| 35 | VfsEntryType GetEntryType(std::string_view path) const override; | ||
| 36 | VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; | ||
| 37 | VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; | ||
| 38 | VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; | ||
| 39 | VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; | ||
| 40 | bool DeleteFile(std::string_view path) override; | ||
| 41 | VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; | ||
| 42 | VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; | ||
| 43 | VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; | ||
| 44 | VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; | ||
| 45 | bool DeleteDirectory(std::string_view path) override; | ||
| 46 | |||
| 47 | private: | ||
| 48 | using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType; | ||
| 49 | std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache; | ||
| 50 | ReferenceListType open_references; | ||
| 51 | ReferenceListType closed_references; | ||
| 52 | std::mutex list_lock; | ||
| 53 | size_t num_open_files{}; | ||
| 54 | |||
| 55 | private: | ||
| 56 | friend class RealVfsFile; | ||
| 57 | std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms, | ||
| 58 | FileReference& reference); | ||
| 59 | void DropReference(std::unique_ptr<FileReference>&& reference); | ||
| 60 | |||
| 61 | private: | ||
| 62 | friend class RealVfsDirectory; | ||
| 63 | VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size, | ||
| 64 | Mode perms = Mode::Read); | ||
| 65 | |||
| 66 | private: | ||
| 67 | void EvictSingleReferenceLocked(); | ||
| 68 | void InsertReferenceIntoListLocked(FileReference& reference); | ||
| 69 | void RemoveReferenceFromListLocked(FileReference& reference); | ||
| 70 | }; | ||
| 71 | |||
| 72 | // An implementation of VfsFile that represents a file on the user's computer. | ||
| 73 | class RealVfsFile : public VfsFile { | ||
| 74 | friend class RealVfsDirectory; | ||
| 75 | friend class RealVfsFilesystem; | ||
| 76 | |||
| 77 | public: | ||
| 78 | ~RealVfsFile() override; | ||
| 79 | |||
| 80 | std::string GetName() const override; | ||
| 81 | std::size_t GetSize() const override; | ||
| 82 | bool Resize(std::size_t new_size) override; | ||
| 83 | VirtualDir GetContainingDirectory() const override; | ||
| 84 | bool IsWritable() const override; | ||
| 85 | bool IsReadable() const override; | ||
| 86 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | ||
| 87 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; | ||
| 88 | bool Rename(std::string_view name) override; | ||
| 89 | |||
| 90 | private: | ||
| 91 | RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, | ||
| 92 | const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {}); | ||
| 93 | |||
| 94 | RealVfsFilesystem& base; | ||
| 95 | std::unique_ptr<FileReference> reference; | ||
| 96 | std::string path; | ||
| 97 | std::string parent_path; | ||
| 98 | std::vector<std::string> path_components; | ||
| 99 | std::optional<u64> size; | ||
| 100 | Mode perms; | ||
| 101 | }; | ||
| 102 | |||
| 103 | // An implementation of VfsDirectory that represents a directory on the user's computer. | ||
| 104 | class RealVfsDirectory : public VfsDirectory { | ||
| 105 | friend class RealVfsFilesystem; | ||
| 106 | |||
| 107 | public: | ||
| 108 | ~RealVfsDirectory() override; | ||
| 109 | |||
| 110 | VirtualFile GetFileRelative(std::string_view relative_path) const override; | ||
| 111 | VirtualDir GetDirectoryRelative(std::string_view relative_path) const override; | ||
| 112 | VirtualFile GetFile(std::string_view name) const override; | ||
| 113 | VirtualDir GetSubdirectory(std::string_view name) const override; | ||
| 114 | VirtualFile CreateFileRelative(std::string_view relative_path) override; | ||
| 115 | VirtualDir CreateDirectoryRelative(std::string_view relative_path) override; | ||
| 116 | bool DeleteSubdirectoryRecursive(std::string_view name) override; | ||
| 117 | std::vector<VirtualFile> GetFiles() const override; | ||
| 118 | FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override; | ||
| 119 | std::vector<VirtualDir> GetSubdirectories() const override; | ||
| 120 | bool IsWritable() const override; | ||
| 121 | bool IsReadable() const override; | ||
| 122 | std::string GetName() const override; | ||
| 123 | VirtualDir GetParentDirectory() const override; | ||
| 124 | VirtualDir CreateSubdirectory(std::string_view name) override; | ||
| 125 | VirtualFile CreateFile(std::string_view name) override; | ||
| 126 | bool DeleteSubdirectory(std::string_view name) override; | ||
| 127 | bool DeleteFile(std::string_view name) override; | ||
| 128 | bool Rename(std::string_view name) override; | ||
| 129 | std::string GetFullPath() const override; | ||
| 130 | std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; | ||
| 131 | |||
| 132 | private: | ||
| 133 | RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); | ||
| 134 | |||
| 135 | template <typename T, typename R> | ||
| 136 | std::vector<std::shared_ptr<R>> IterateEntries() const; | ||
| 137 | |||
| 138 | RealVfsFilesystem& base; | ||
| 139 | std::string path; | ||
| 140 | std::string parent_path; | ||
| 141 | std::vector<std::string> path_components; | ||
| 142 | Mode perms; | ||
| 143 | }; | ||
| 144 | |||
| 145 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_static.h b/src/core/file_sys/vfs/vfs_static.h new file mode 100644 index 000000000..bb53560ac --- /dev/null +++ b/src/core/file_sys/vfs/vfs_static.h | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <algorithm> | ||
| 7 | #include <memory> | ||
| 8 | #include <string_view> | ||
| 9 | |||
| 10 | #include "core/file_sys/vfs/vfs.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | class StaticVfsFile : public VfsFile { | ||
| 15 | public: | ||
| 16 | explicit StaticVfsFile(u8 value_, std::size_t size_ = 0, std::string name_ = "", | ||
| 17 | VirtualDir parent_ = nullptr) | ||
| 18 | : value{value_}, size{size_}, name{std::move(name_)}, parent{std::move(parent_)} {} | ||
| 19 | |||
| 20 | std::string GetName() const override { | ||
| 21 | return name; | ||
| 22 | } | ||
| 23 | |||
| 24 | std::size_t GetSize() const override { | ||
| 25 | return size; | ||
| 26 | } | ||
| 27 | |||
| 28 | bool Resize(std::size_t new_size) override { | ||
| 29 | size = new_size; | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | VirtualDir GetContainingDirectory() const override { | ||
| 34 | return parent; | ||
| 35 | } | ||
| 36 | |||
| 37 | bool IsWritable() const override { | ||
| 38 | return false; | ||
| 39 | } | ||
| 40 | |||
| 41 | bool IsReadable() const override { | ||
| 42 | return true; | ||
| 43 | } | ||
| 44 | |||
| 45 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override { | ||
| 46 | const auto read = std::min(length, size - offset); | ||
| 47 | std::fill(data, data + read, value); | ||
| 48 | return read; | ||
| 49 | } | ||
| 50 | |||
| 51 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { | ||
| 52 | return 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | std::optional<u8> ReadByte(std::size_t offset) const override { | ||
| 56 | if (offset >= size) { | ||
| 57 | return std::nullopt; | ||
| 58 | } | ||
| 59 | |||
| 60 | return value; | ||
| 61 | } | ||
| 62 | |||
| 63 | std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { | ||
| 64 | const auto read = std::min(length, size - offset); | ||
| 65 | return std::vector<u8>(read, value); | ||
| 66 | } | ||
| 67 | |||
| 68 | bool Rename(std::string_view new_name) override { | ||
| 69 | name = new_name; | ||
| 70 | return true; | ||
| 71 | } | ||
| 72 | |||
| 73 | private: | ||
| 74 | u8 value; | ||
| 75 | std::size_t size; | ||
| 76 | std::string name; | ||
| 77 | VirtualDir parent; | ||
| 78 | }; | ||
| 79 | |||
| 80 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_types.h b/src/core/file_sys/vfs/vfs_types.h new file mode 100644 index 000000000..4a583ed64 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_types.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | class VfsDirectory; | ||
| 13 | class VfsFile; | ||
| 14 | class VfsFilesystem; | ||
| 15 | |||
| 16 | // Declarations for Vfs* pointer types | ||
| 17 | |||
| 18 | using VirtualDir = std::shared_ptr<VfsDirectory>; | ||
| 19 | using VirtualFile = std::shared_ptr<VfsFile>; | ||
| 20 | using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; | ||
| 21 | |||
| 22 | struct FileTimeStampRaw { | ||
| 23 | u64 created{}; | ||
| 24 | u64 accessed{}; | ||
| 25 | u64 modified{}; | ||
| 26 | u64 padding{}; | ||
| 27 | }; | ||
| 28 | |||
| 29 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_vector.cpp b/src/core/file_sys/vfs/vfs_vector.cpp new file mode 100644 index 000000000..0d54461c8 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_vector.cpp | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <utility> | ||
| 6 | #include "core/file_sys/vfs/vfs_vector.h" | ||
| 7 | |||
| 8 | namespace FileSys { | ||
| 9 | VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name_, VirtualDir parent_) | ||
| 10 | : data(std::move(initial_data)), parent(std::move(parent_)), name(std::move(name_)) {} | ||
| 11 | |||
| 12 | VectorVfsFile::~VectorVfsFile() = default; | ||
| 13 | |||
| 14 | std::string VectorVfsFile::GetName() const { | ||
| 15 | return name; | ||
| 16 | } | ||
| 17 | |||
| 18 | size_t VectorVfsFile::GetSize() const { | ||
| 19 | return data.size(); | ||
| 20 | } | ||
| 21 | |||
| 22 | bool VectorVfsFile::Resize(size_t new_size) { | ||
| 23 | data.resize(new_size); | ||
| 24 | return true; | ||
| 25 | } | ||
| 26 | |||
| 27 | VirtualDir VectorVfsFile::GetContainingDirectory() const { | ||
| 28 | return parent; | ||
| 29 | } | ||
| 30 | |||
| 31 | bool VectorVfsFile::IsWritable() const { | ||
| 32 | return true; | ||
| 33 | } | ||
| 34 | |||
| 35 | bool VectorVfsFile::IsReadable() const { | ||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 39 | std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const { | ||
| 40 | const auto read = std::min(length, data.size() - offset); | ||
| 41 | std::memcpy(data_, data.data() + offset, read); | ||
| 42 | return read; | ||
| 43 | } | ||
| 44 | |||
| 45 | std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) { | ||
| 46 | if (offset + length > data.size()) | ||
| 47 | data.resize(offset + length); | ||
| 48 | const auto write = std::min(length, data.size() - offset); | ||
| 49 | std::memcpy(data.data() + offset, data_, write); | ||
| 50 | return write; | ||
| 51 | } | ||
| 52 | |||
| 53 | bool VectorVfsFile::Rename(std::string_view name_) { | ||
| 54 | name = name_; | ||
| 55 | return true; | ||
| 56 | } | ||
| 57 | |||
| 58 | void VectorVfsFile::Assign(std::vector<u8> new_data) { | ||
| 59 | data = std::move(new_data); | ||
| 60 | } | ||
| 61 | |||
| 62 | VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, | ||
| 63 | std::vector<VirtualDir> dirs_, std::string name_, | ||
| 64 | VirtualDir parent_) | ||
| 65 | : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), | ||
| 66 | name(std::move(name_)) {} | ||
| 67 | |||
| 68 | VectorVfsDirectory::~VectorVfsDirectory() = default; | ||
| 69 | |||
| 70 | std::vector<VirtualFile> VectorVfsDirectory::GetFiles() const { | ||
| 71 | return files; | ||
| 72 | } | ||
| 73 | |||
| 74 | std::vector<VirtualDir> VectorVfsDirectory::GetSubdirectories() const { | ||
| 75 | return dirs; | ||
| 76 | } | ||
| 77 | |||
| 78 | bool VectorVfsDirectory::IsWritable() const { | ||
| 79 | return false; | ||
| 80 | } | ||
| 81 | |||
| 82 | bool VectorVfsDirectory::IsReadable() const { | ||
| 83 | return true; | ||
| 84 | } | ||
| 85 | |||
| 86 | std::string VectorVfsDirectory::GetName() const { | ||
| 87 | return name; | ||
| 88 | } | ||
| 89 | |||
| 90 | VirtualDir VectorVfsDirectory::GetParentDirectory() const { | ||
| 91 | return parent; | ||
| 92 | } | ||
| 93 | |||
| 94 | template <typename T> | ||
| 95 | static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) { | ||
| 96 | const auto iter = | ||
| 97 | std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; }); | ||
| 98 | if (iter == vec.end()) | ||
| 99 | return false; | ||
| 100 | |||
| 101 | vec.erase(iter); | ||
| 102 | return true; | ||
| 103 | } | ||
| 104 | |||
| 105 | bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { | ||
| 106 | return FindAndRemoveVectorElement(dirs, subdir_name); | ||
| 107 | } | ||
| 108 | |||
| 109 | bool VectorVfsDirectory::DeleteFile(std::string_view file_name) { | ||
| 110 | return FindAndRemoveVectorElement(files, file_name); | ||
| 111 | } | ||
| 112 | |||
| 113 | bool VectorVfsDirectory::Rename(std::string_view name_) { | ||
| 114 | name = name_; | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | |||
| 118 | VirtualDir VectorVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { | ||
| 119 | return nullptr; | ||
| 120 | } | ||
| 121 | |||
| 122 | VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) { | ||
| 123 | return nullptr; | ||
| 124 | } | ||
| 125 | |||
| 126 | void VectorVfsDirectory::AddFile(VirtualFile file) { | ||
| 127 | files.push_back(std::move(file)); | ||
| 128 | } | ||
| 129 | |||
| 130 | void VectorVfsDirectory::AddDirectory(VirtualDir dir) { | ||
| 131 | dirs.push_back(std::move(dir)); | ||
| 132 | } | ||
| 133 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs/vfs_vector.h b/src/core/file_sys/vfs/vfs_vector.h new file mode 100644 index 000000000..587187dd2 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_vector.h | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <cstring> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <vector> | ||
| 11 | #include "core/file_sys/vfs/vfs.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | // An implementation of VfsFile that is backed by a statically-sized array | ||
| 16 | template <std::size_t size> | ||
| 17 | class ArrayVfsFile : public VfsFile { | ||
| 18 | public: | ||
| 19 | explicit ArrayVfsFile(const std::array<u8, size>& data_, std::string name_ = "", | ||
| 20 | VirtualDir parent_ = nullptr) | ||
| 21 | : data(data_), name(std::move(name_)), parent(std::move(parent_)) {} | ||
| 22 | |||
| 23 | std::string GetName() const override { | ||
| 24 | return name; | ||
| 25 | } | ||
| 26 | |||
| 27 | std::size_t GetSize() const override { | ||
| 28 | return size; | ||
| 29 | } | ||
| 30 | |||
| 31 | bool Resize(std::size_t new_size) override { | ||
| 32 | return false; | ||
| 33 | } | ||
| 34 | |||
| 35 | VirtualDir GetContainingDirectory() const override { | ||
| 36 | return parent; | ||
| 37 | } | ||
| 38 | |||
| 39 | bool IsWritable() const override { | ||
| 40 | return false; | ||
| 41 | } | ||
| 42 | |||
| 43 | bool IsReadable() const override { | ||
| 44 | return true; | ||
| 45 | } | ||
| 46 | |||
| 47 | std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override { | ||
| 48 | const auto read = std::min(length, size - offset); | ||
| 49 | std::memcpy(data_, data.data() + offset, read); | ||
| 50 | return read; | ||
| 51 | } | ||
| 52 | |||
| 53 | std::size_t Write(const u8* data_, std::size_t length, std::size_t offset) override { | ||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | |||
| 57 | bool Rename(std::string_view new_name) override { | ||
| 58 | name = new_name; | ||
| 59 | return true; | ||
| 60 | } | ||
| 61 | |||
| 62 | private: | ||
| 63 | std::array<u8, size> data; | ||
| 64 | std::string name; | ||
| 65 | VirtualDir parent; | ||
| 66 | }; | ||
| 67 | |||
| 68 | template <std::size_t Size, typename... Args> | ||
| 69 | std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data, | ||
| 70 | Args&&... args) { | ||
| 71 | return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...); | ||
| 72 | } | ||
| 73 | |||
| 74 | // An implementation of VfsFile that is backed by a vector optionally supplied upon construction | ||
| 75 | class VectorVfsFile : public VfsFile { | ||
| 76 | public: | ||
| 77 | explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name_ = "", | ||
| 78 | VirtualDir parent_ = nullptr); | ||
| 79 | ~VectorVfsFile() override; | ||
| 80 | |||
| 81 | std::string GetName() const override; | ||
| 82 | std::size_t GetSize() const override; | ||
| 83 | bool Resize(std::size_t new_size) override; | ||
| 84 | VirtualDir GetContainingDirectory() const override; | ||
| 85 | bool IsWritable() const override; | ||
| 86 | bool IsReadable() const override; | ||
| 87 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | ||
| 88 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; | ||
| 89 | bool Rename(std::string_view name) override; | ||
| 90 | |||
| 91 | virtual void Assign(std::vector<u8> new_data); | ||
| 92 | |||
| 93 | private: | ||
| 94 | std::vector<u8> data; | ||
| 95 | VirtualDir parent; | ||
| 96 | std::string name; | ||
| 97 | }; | ||
| 98 | |||
| 99 | // An implementation of VfsDirectory that maintains two vectors for subdirectories and files. | ||
| 100 | // Vector data is supplied upon construction. | ||
| 101 | class VectorVfsDirectory : public VfsDirectory { | ||
| 102 | public: | ||
| 103 | explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, | ||
| 104 | std::vector<VirtualDir> dirs = {}, std::string name = "", | ||
| 105 | VirtualDir parent = nullptr); | ||
| 106 | ~VectorVfsDirectory() override; | ||
| 107 | |||
| 108 | std::vector<VirtualFile> GetFiles() const override; | ||
| 109 | std::vector<VirtualDir> GetSubdirectories() const override; | ||
| 110 | bool IsWritable() const override; | ||
| 111 | bool IsReadable() const override; | ||
| 112 | std::string GetName() const override; | ||
| 113 | VirtualDir GetParentDirectory() const override; | ||
| 114 | bool DeleteSubdirectory(std::string_view subdir_name) override; | ||
| 115 | bool DeleteFile(std::string_view file_name) override; | ||
| 116 | bool Rename(std::string_view name) override; | ||
| 117 | VirtualDir CreateSubdirectory(std::string_view subdir_name) override; | ||
| 118 | VirtualFile CreateFile(std::string_view file_name) override; | ||
| 119 | |||
| 120 | virtual void AddFile(VirtualFile file); | ||
| 121 | virtual void AddDirectory(VirtualDir dir); | ||
| 122 | |||
| 123 | private: | ||
| 124 | std::vector<VirtualFile> files; | ||
| 125 | std::vector<VirtualDir> dirs; | ||
| 126 | |||
| 127 | VirtualDir parent; | ||
| 128 | std::string name; | ||
| 129 | }; | ||
| 130 | |||
| 131 | } // namespace FileSys | ||