1 // Copyright 2018 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <utility>
6
7 #include "common/assert.h"
8 #include "common/file_util.h"
9 #include "core/core.h"
10 #include "core/file_sys/bis_factory.h"
11 #include "core/file_sys/card_image.h"
12 #include "core/file_sys/control_metadata.h"
13 #include "core/file_sys/errors.h"
14 #include "core/file_sys/mode.h"
15 #include "core/file_sys/partition_filesystem.h"
16 #include "core/file_sys/patch_manager.h"
17 #include "core/file_sys/registered_cache.h"
18 #include "core/file_sys/romfs_factory.h"
19 #include "core/file_sys/savedata_factory.h"
20 #include "core/file_sys/sdmc_factory.h"
21 #include "core/file_sys/vfs.h"
22 #include "core/file_sys/vfs_offset.h"
23 #include "core/hle/kernel/process.h"
24 #include "core/hle/service/filesystem/filesystem.h"
25 #include "core/hle/service/filesystem/fsp_ldr.h"
26 #include "core/hle/service/filesystem/fsp_pr.h"
27 #include "core/hle/service/filesystem/fsp_srv.h"
28 #include "core/loader/loader.h"
29 #include "core/settings.h"
30
31 namespace Service::FileSystem {
32
33 // A default size for normal/journal save data size if application control metadata cannot be found.
34 // This should be large enough to satisfy even the most extreme requirements (~4.2GB)
35 constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
36
GetDirectoryRelativeWrapped(FileSys::VirtualDir base,std::string_view dir_name_)37 static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
38 std::string_view dir_name_) {
39 std::string dir_name(Common::FS::SanitizePath(dir_name_));
40 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
41 return base;
42
43 return base->GetDirectoryRelative(dir_name);
44 }
45
VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)46 VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
47 : backing(std::move(backing_)) {}
48
49 VfsDirectoryServiceWrapper::~VfsDirectoryServiceWrapper() = default;
50
GetName() const51 std::string VfsDirectoryServiceWrapper::GetName() const {
52 return backing->GetName();
53 }
54
CreateFile(const std::string & path_,u64 size) const55 ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
56 std::string path(Common::FS::SanitizePath(path_));
57 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
58 // dir can be nullptr if path contains subdirectories, create those prior to creating the file.
59 if (dir == nullptr) {
60 dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path));
61 }
62 auto file = dir->CreateFile(Common::FS::GetFilename(path));
63 if (file == nullptr) {
64 // TODO(DarkLordZach): Find a better error code for this
65 return RESULT_UNKNOWN;
66 }
67 if (!file->Resize(size)) {
68 // TODO(DarkLordZach): Find a better error code for this
69 return RESULT_UNKNOWN;
70 }
71 return RESULT_SUCCESS;
72 }
73
DeleteFile(const std::string & path_) const74 ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
75 std::string path(Common::FS::SanitizePath(path_));
76 if (path.empty()) {
77 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
78 return RESULT_SUCCESS;
79 }
80
81 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
82 if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
83 return FileSys::ERROR_PATH_NOT_FOUND;
84 }
85 if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
86 // TODO(DarkLordZach): Find a better error code for this
87 return RESULT_UNKNOWN;
88 }
89
90 return RESULT_SUCCESS;
91 }
92
CreateDirectory(const std::string & path_) const93 ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
94 std::string path(Common::FS::SanitizePath(path_));
95 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
96 if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) {
97 dir = backing;
98 }
99 auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
100 if (new_dir == nullptr) {
101 // TODO(DarkLordZach): Find a better error code for this
102 return RESULT_UNKNOWN;
103 }
104 return RESULT_SUCCESS;
105 }
106
DeleteDirectory(const std::string & path_) const107 ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
108 std::string path(Common::FS::SanitizePath(path_));
109 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
110 if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
111 // TODO(DarkLordZach): Find a better error code for this
112 return RESULT_UNKNOWN;
113 }
114 return RESULT_SUCCESS;
115 }
116
DeleteDirectoryRecursively(const std::string & path_) const117 ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
118 std::string path(Common::FS::SanitizePath(path_));
119 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
120 if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
121 // TODO(DarkLordZach): Find a better error code for this
122 return RESULT_UNKNOWN;
123 }
124 return RESULT_SUCCESS;
125 }
126
CleanDirectoryRecursively(const std::string & path) const127 ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
128 const std::string sanitized_path(Common::FS::SanitizePath(path));
129 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
130
131 if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
132 // TODO(DarkLordZach): Find a better error code for this
133 return RESULT_UNKNOWN;
134 }
135
136 return RESULT_SUCCESS;
137 }
138
RenameFile(const std::string & src_path_,const std::string & dest_path_) const139 ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
140 const std::string& dest_path_) const {
141 std::string src_path(Common::FS::SanitizePath(src_path_));
142 std::string dest_path(Common::FS::SanitizePath(dest_path_));
143 auto src = backing->GetFileRelative(src_path);
144 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
145 // Use more-optimized vfs implementation rename.
146 if (src == nullptr)
147 return FileSys::ERROR_PATH_NOT_FOUND;
148 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
149 // TODO(DarkLordZach): Find a better error code for this
150 return RESULT_UNKNOWN;
151 }
152 return RESULT_SUCCESS;
153 }
154
155 // Move by hand -- TODO(DarkLordZach): Optimize
156 auto c_res = CreateFile(dest_path, src->GetSize());
157 if (c_res != RESULT_SUCCESS)
158 return c_res;
159
160 auto dest = backing->GetFileRelative(dest_path);
161 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
162
163 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
164 "Could not write all of the bytes but everything else has succeded.");
165
166 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
167 // TODO(DarkLordZach): Find a better error code for this
168 return RESULT_UNKNOWN;
169 }
170
171 return RESULT_SUCCESS;
172 }
173
RenameDirectory(const std::string & src_path_,const std::string & dest_path_) const174 ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
175 const std::string& dest_path_) const {
176 std::string src_path(Common::FS::SanitizePath(src_path_));
177 std::string dest_path(Common::FS::SanitizePath(dest_path_));
178 auto src = GetDirectoryRelativeWrapped(backing, src_path);
179 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
180 // Use more-optimized vfs implementation rename.
181 if (src == nullptr)
182 return FileSys::ERROR_PATH_NOT_FOUND;
183 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
184 // TODO(DarkLordZach): Find a better error code for this
185 return RESULT_UNKNOWN;
186 }
187 return RESULT_SUCCESS;
188 }
189
190 // TODO(DarkLordZach): Implement renaming across the tree (move).
191 ASSERT_MSG(false,
192 "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
193 "don't match -- UNIMPLEMENTED",
194 src_path, dest_path);
195
196 // TODO(DarkLordZach): Find a better error code for this
197 return RESULT_UNKNOWN;
198 }
199
OpenFile(const std::string & path_,FileSys::Mode mode) const200 ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
201 FileSys::Mode mode) const {
202 const std::string path(Common::FS::SanitizePath(path_));
203 std::string_view npath = path;
204 while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
205 npath.remove_prefix(1);
206 }
207
208 auto file = backing->GetFileRelative(npath);
209 if (file == nullptr) {
210 return FileSys::ERROR_PATH_NOT_FOUND;
211 }
212
213 if (mode == FileSys::Mode::Append) {
214 return MakeResult<FileSys::VirtualFile>(
215 std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
216 }
217
218 return MakeResult<FileSys::VirtualFile>(file);
219 }
220
OpenDirectory(const std::string & path_)221 ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
222 std::string path(Common::FS::SanitizePath(path_));
223 auto dir = GetDirectoryRelativeWrapped(backing, path);
224 if (dir == nullptr) {
225 // TODO(DarkLordZach): Find a better error code for this
226 return FileSys::ERROR_PATH_NOT_FOUND;
227 }
228 return MakeResult(dir);
229 }
230
GetEntryType(const std::string & path_) const231 ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
232 const std::string& path_) const {
233 std::string path(Common::FS::SanitizePath(path_));
234 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
235 if (dir == nullptr)
236 return FileSys::ERROR_PATH_NOT_FOUND;
237 auto filename = Common::FS::GetFilename(path);
238 // TODO(Subv): Some games use the '/' path, find out what this means.
239 if (filename.empty())
240 return MakeResult(FileSys::EntryType::Directory);
241
242 if (dir->GetFile(filename) != nullptr)
243 return MakeResult(FileSys::EntryType::File);
244 if (dir->GetSubdirectory(filename) != nullptr)
245 return MakeResult(FileSys::EntryType::Directory);
246 return FileSys::ERROR_PATH_NOT_FOUND;
247 }
248
FileSystemController(Core::System & system_)249 FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
250
251 FileSystemController::~FileSystemController() = default;
252
RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory> && factory)253 ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
254 romfs_factory = std::move(factory);
255 LOG_DEBUG(Service_FS, "Registered RomFS");
256 return RESULT_SUCCESS;
257 }
258
RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory> && factory)259 ResultCode FileSystemController::RegisterSaveData(
260 std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
261 ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
262 save_data_factory = std::move(factory);
263 LOG_DEBUG(Service_FS, "Registered save data");
264 return RESULT_SUCCESS;
265 }
266
RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory> && factory)267 ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
268 ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
269 sdmc_factory = std::move(factory);
270 LOG_DEBUG(Service_FS, "Registered SDMC");
271 return RESULT_SUCCESS;
272 }
273
RegisterBIS(std::unique_ptr<FileSys::BISFactory> && factory)274 ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
275 ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
276 bis_factory = std::move(factory);
277 LOG_DEBUG(Service_FS, "Registered BIS");
278 return RESULT_SUCCESS;
279 }
280
SetPackedUpdate(FileSys::VirtualFile update_raw)281 void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
282 LOG_TRACE(Service_FS, "Setting packed update for romfs");
283
284 if (romfs_factory == nullptr)
285 return;
286
287 romfs_factory->SetPackedUpdate(std::move(update_raw));
288 }
289
OpenRomFSCurrentProcess() const290 ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const {
291 LOG_TRACE(Service_FS, "Opening RomFS for current process");
292
293 if (romfs_factory == nullptr) {
294 // TODO(bunnei): Find a better error code for this
295 return RESULT_UNKNOWN;
296 }
297
298 return romfs_factory->OpenCurrentProcess(system.CurrentProcess()->GetTitleID());
299 }
300
OpenPatchedRomFS(u64 title_id,FileSys::ContentRecordType type) const301 ResultVal<FileSys::VirtualFile> FileSystemController::OpenPatchedRomFS(
302 u64 title_id, FileSys::ContentRecordType type) const {
303 LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id);
304
305 if (romfs_factory == nullptr) {
306 // TODO: Find a better error code for this
307 return RESULT_UNKNOWN;
308 }
309
310 return romfs_factory->OpenPatchedRomFS(title_id, type);
311 }
312
OpenPatchedRomFSWithProgramIndex(u64 title_id,u8 program_index,FileSys::ContentRecordType type) const313 ResultVal<FileSys::VirtualFile> FileSystemController::OpenPatchedRomFSWithProgramIndex(
314 u64 title_id, u8 program_index, FileSys::ContentRecordType type) const {
315 LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id,
316 program_index);
317
318 if (romfs_factory == nullptr) {
319 // TODO: Find a better error code for this
320 return RESULT_UNKNOWN;
321 }
322
323 return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
324 }
325
OpenRomFS(u64 title_id,FileSys::StorageId storage_id,FileSys::ContentRecordType type) const326 ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
327 u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
328 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
329 title_id, storage_id, type);
330
331 if (romfs_factory == nullptr) {
332 // TODO(bunnei): Find a better error code for this
333 return RESULT_UNKNOWN;
334 }
335
336 return romfs_factory->Open(title_id, storage_id, type);
337 }
338
CreateSaveData(FileSys::SaveDataSpaceId space,const FileSys::SaveDataAttribute & save_struct) const339 ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
340 FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const {
341 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
342 save_struct.DebugInfo());
343
344 if (save_data_factory == nullptr) {
345 return FileSys::ERROR_ENTITY_NOT_FOUND;
346 }
347
348 return save_data_factory->Create(space, save_struct);
349 }
350
OpenSaveData(FileSys::SaveDataSpaceId space,const FileSys::SaveDataAttribute & attribute) const351 ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
352 FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const {
353 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space,
354 attribute.DebugInfo());
355
356 if (save_data_factory == nullptr) {
357 return FileSys::ERROR_ENTITY_NOT_FOUND;
358 }
359
360 return save_data_factory->Open(space, attribute);
361 }
362
OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const363 ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
364 FileSys::SaveDataSpaceId space) const {
365 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space);
366
367 if (save_data_factory == nullptr) {
368 return FileSys::ERROR_ENTITY_NOT_FOUND;
369 }
370
371 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
372 }
373
OpenSDMC() const374 ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const {
375 LOG_TRACE(Service_FS, "Opening SDMC");
376
377 if (sdmc_factory == nullptr) {
378 return FileSys::ERROR_SD_CARD_NOT_FOUND;
379 }
380
381 return sdmc_factory->Open();
382 }
383
OpenBISPartition(FileSys::BisPartitionId id) const384 ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
385 FileSys::BisPartitionId id) const {
386 LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id);
387
388 if (bis_factory == nullptr) {
389 return FileSys::ERROR_ENTITY_NOT_FOUND;
390 }
391
392 auto part = bis_factory->OpenPartition(id);
393 if (part == nullptr) {
394 return FileSys::ERROR_INVALID_ARGUMENT;
395 }
396
397 return MakeResult<FileSys::VirtualDir>(std::move(part));
398 }
399
OpenBISPartitionStorage(FileSys::BisPartitionId id) const400 ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
401 FileSys::BisPartitionId id) const {
402 LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id);
403
404 if (bis_factory == nullptr) {
405 return FileSys::ERROR_ENTITY_NOT_FOUND;
406 }
407
408 auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
409 if (part == nullptr) {
410 return FileSys::ERROR_INVALID_ARGUMENT;
411 }
412
413 return MakeResult<FileSys::VirtualFile>(std::move(part));
414 }
415
GetFreeSpaceSize(FileSys::StorageId id) const416 u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
417 switch (id) {
418 case FileSys::StorageId::None:
419 case FileSys::StorageId::GameCard:
420 return 0;
421 case FileSys::StorageId::SdCard:
422 if (sdmc_factory == nullptr)
423 return 0;
424 return sdmc_factory->GetSDMCFreeSpace();
425 case FileSys::StorageId::Host:
426 if (bis_factory == nullptr)
427 return 0;
428 return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace();
429 case FileSys::StorageId::NandSystem:
430 if (bis_factory == nullptr)
431 return 0;
432 return bis_factory->GetSystemNANDFreeSpace();
433 case FileSys::StorageId::NandUser:
434 if (bis_factory == nullptr)
435 return 0;
436 return bis_factory->GetUserNANDFreeSpace();
437 }
438
439 return 0;
440 }
441
GetTotalSpaceSize(FileSys::StorageId id) const442 u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
443 switch (id) {
444 case FileSys::StorageId::None:
445 case FileSys::StorageId::GameCard:
446 return 0;
447 case FileSys::StorageId::SdCard:
448 if (sdmc_factory == nullptr)
449 return 0;
450 return sdmc_factory->GetSDMCTotalSpace();
451 case FileSys::StorageId::Host:
452 if (bis_factory == nullptr)
453 return 0;
454 return bis_factory->GetFullNANDTotalSpace();
455 case FileSys::StorageId::NandSystem:
456 if (bis_factory == nullptr)
457 return 0;
458 return bis_factory->GetSystemNANDTotalSpace();
459 case FileSys::StorageId::NandUser:
460 if (bis_factory == nullptr)
461 return 0;
462 return bis_factory->GetUserNANDTotalSpace();
463 }
464
465 return 0;
466 }
467
ReadSaveDataSize(FileSys::SaveDataType type,u64 title_id,u128 user_id) const468 FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
469 u64 title_id, u128 user_id) const {
470 if (save_data_factory == nullptr) {
471 return {0, 0};
472 }
473
474 const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
475
476 if (value.normal == 0 && value.journal == 0) {
477 FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
478
479 FileSys::NACP nacp;
480 const auto res = system.GetAppLoader().ReadControlData(nacp);
481
482 if (res != Loader::ResultStatus::Success) {
483 const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
484 system.GetFileSystemController(),
485 system.GetContentProvider()};
486 const auto metadata = pm.GetControlMetadata();
487 const auto& nacp_unique = metadata.first;
488
489 if (nacp_unique != nullptr) {
490 new_size = {nacp_unique->GetDefaultNormalSaveSize(),
491 nacp_unique->GetDefaultJournalSaveSize()};
492 }
493 } else {
494 new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
495 }
496
497 WriteSaveDataSize(type, title_id, user_id, new_size);
498 return new_size;
499 }
500
501 return value;
502 }
503
WriteSaveDataSize(FileSys::SaveDataType type,u64 title_id,u128 user_id,FileSys::SaveDataSize new_value) const504 void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
505 FileSys::SaveDataSize new_value) const {
506 if (save_data_factory != nullptr)
507 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
508 }
509
SetGameCard(FileSys::VirtualFile file)510 void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
511 gamecard = std::make_unique<FileSys::XCI>(file);
512 const auto dir = gamecard->ConcatenatedPseudoDirectory();
513 gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir);
514 gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
515 }
516
GetGameCard() const517 FileSys::XCI* FileSystemController::GetGameCard() const {
518 return gamecard.get();
519 }
520
GetGameCardContents() const521 FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const {
522 return gamecard_registered.get();
523 }
524
GetGameCardPlaceholder() const525 FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const {
526 return gamecard_placeholder.get();
527 }
528
GetSystemNANDContents() const529 FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const {
530 LOG_TRACE(Service_FS, "Opening System NAND Contents");
531
532 if (bis_factory == nullptr)
533 return nullptr;
534
535 return bis_factory->GetSystemNANDContents();
536 }
537
GetUserNANDContents() const538 FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const {
539 LOG_TRACE(Service_FS, "Opening User NAND Contents");
540
541 if (bis_factory == nullptr)
542 return nullptr;
543
544 return bis_factory->GetUserNANDContents();
545 }
546
GetSDMCContents() const547 FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
548 LOG_TRACE(Service_FS, "Opening SDMC Contents");
549
550 if (sdmc_factory == nullptr)
551 return nullptr;
552
553 return sdmc_factory->GetSDMCContents();
554 }
555
GetSystemNANDPlaceholder() const556 FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
557 LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
558
559 if (bis_factory == nullptr)
560 return nullptr;
561
562 return bis_factory->GetSystemNANDPlaceholder();
563 }
564
GetUserNANDPlaceholder() const565 FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const {
566 LOG_TRACE(Service_FS, "Opening User NAND Placeholder");
567
568 if (bis_factory == nullptr)
569 return nullptr;
570
571 return bis_factory->GetUserNANDPlaceholder();
572 }
573
GetSDMCPlaceholder() const574 FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const {
575 LOG_TRACE(Service_FS, "Opening SDMC Placeholder");
576
577 if (sdmc_factory == nullptr)
578 return nullptr;
579
580 return sdmc_factory->GetSDMCPlaceholder();
581 }
582
GetRegisteredCacheForStorage(FileSys::StorageId id) const583 FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
584 FileSys::StorageId id) const {
585 switch (id) {
586 case FileSys::StorageId::None:
587 case FileSys::StorageId::Host:
588 UNIMPLEMENTED();
589 return nullptr;
590 case FileSys::StorageId::GameCard:
591 return GetGameCardContents();
592 case FileSys::StorageId::NandSystem:
593 return GetSystemNANDContents();
594 case FileSys::StorageId::NandUser:
595 return GetUserNANDContents();
596 case FileSys::StorageId::SdCard:
597 return GetSDMCContents();
598 }
599
600 return nullptr;
601 }
602
GetPlaceholderCacheForStorage(FileSys::StorageId id) const603 FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
604 FileSys::StorageId id) const {
605 switch (id) {
606 case FileSys::StorageId::None:
607 case FileSys::StorageId::Host:
608 UNIMPLEMENTED();
609 return nullptr;
610 case FileSys::StorageId::GameCard:
611 return GetGameCardPlaceholder();
612 case FileSys::StorageId::NandSystem:
613 return GetSystemNANDPlaceholder();
614 case FileSys::StorageId::NandUser:
615 return GetUserNANDPlaceholder();
616 case FileSys::StorageId::SdCard:
617 return GetSDMCPlaceholder();
618 }
619
620 return nullptr;
621 }
622
GetSystemNANDContentDirectory() const623 FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const {
624 LOG_TRACE(Service_FS, "Opening system NAND content directory");
625
626 if (bis_factory == nullptr)
627 return nullptr;
628
629 return bis_factory->GetSystemNANDContentDirectory();
630 }
631
GetUserNANDContentDirectory() const632 FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const {
633 LOG_TRACE(Service_FS, "Opening user NAND content directory");
634
635 if (bis_factory == nullptr)
636 return nullptr;
637
638 return bis_factory->GetUserNANDContentDirectory();
639 }
640
GetSDMCContentDirectory() const641 FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const {
642 LOG_TRACE(Service_FS, "Opening SDMC content directory");
643
644 if (sdmc_factory == nullptr)
645 return nullptr;
646
647 return sdmc_factory->GetSDMCContentDirectory();
648 }
649
GetNANDImageDirectory() const650 FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const {
651 LOG_TRACE(Service_FS, "Opening NAND image directory");
652
653 if (bis_factory == nullptr)
654 return nullptr;
655
656 return bis_factory->GetImageDirectory();
657 }
658
GetSDMCImageDirectory() const659 FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const {
660 LOG_TRACE(Service_FS, "Opening SDMC image directory");
661
662 if (sdmc_factory == nullptr)
663 return nullptr;
664
665 return sdmc_factory->GetImageDirectory();
666 }
667
GetContentDirectory(ContentStorageId id) const668 FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const {
669 switch (id) {
670 case ContentStorageId::System:
671 return GetSystemNANDContentDirectory();
672 case ContentStorageId::User:
673 return GetUserNANDContentDirectory();
674 case ContentStorageId::SdCard:
675 return GetSDMCContentDirectory();
676 }
677
678 return nullptr;
679 }
680
GetImageDirectory(ImageDirectoryId id) const681 FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const {
682 switch (id) {
683 case ImageDirectoryId::NAND:
684 return GetNANDImageDirectory();
685 case ImageDirectoryId::SdCard:
686 return GetSDMCImageDirectory();
687 }
688
689 return nullptr;
690 }
691
GetModificationLoadRoot(u64 title_id) const692 FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const {
693 LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
694
695 if (bis_factory == nullptr)
696 return nullptr;
697
698 return bis_factory->GetModificationLoadRoot(title_id);
699 }
700
GetModificationDumpRoot(u64 title_id) const701 FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
702 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
703
704 if (bis_factory == nullptr)
705 return nullptr;
706
707 return bis_factory->GetModificationDumpRoot(title_id);
708 }
709
GetBCATDirectory(u64 title_id) const710 FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
711 LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id);
712
713 if (bis_factory == nullptr)
714 return nullptr;
715
716 return bis_factory->GetBCATDirectory(title_id);
717 }
718
CreateFactories(FileSys::VfsFilesystem & vfs,bool overwrite)719 void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
720 if (overwrite) {
721 bis_factory = nullptr;
722 save_data_factory = nullptr;
723 sdmc_factory = nullptr;
724 }
725
726 auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
727 FileSys::Mode::ReadWrite);
728 auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir),
729 FileSys::Mode::ReadWrite);
730 auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
731 FileSys::Mode::ReadWrite);
732 auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
733 FileSys::Mode::ReadWrite);
734
735 if (bis_factory == nullptr) {
736 bis_factory =
737 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
738 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
739 bis_factory->GetSystemNANDContents());
740 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
741 bis_factory->GetUserNANDContents());
742 }
743
744 if (save_data_factory == nullptr) {
745 save_data_factory =
746 std::make_unique<FileSys::SaveDataFactory>(system, std::move(nand_directory));
747 }
748
749 if (sdmc_factory == nullptr) {
750 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
751 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
752 sdmc_factory->GetSDMCContents());
753 }
754 }
755
InstallInterfaces(Core::System & system)756 void InstallInterfaces(Core::System& system) {
757 std::make_shared<FSP_LDR>(system)->InstallAsService(system.ServiceManager());
758 std::make_shared<FSP_PR>(system)->InstallAsService(system.ServiceManager());
759 std::make_shared<FSP_SRV>(system)->InstallAsService(system.ServiceManager());
760 }
761
762 } // namespace Service::FileSystem
763