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