mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #2430 from DarkLordZach/fs-controller
core: Implement FileSystemController to deglobalize FS services
This commit is contained in:
		
						commit
						c9ccdfbeac
					
				| @ -14,8 +14,13 @@ | ||||
| #include "core/core_cpu.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/cpu_core_manager.h" | ||||
| #include "core/file_sys/bis_factory.h" | ||||
| #include "core/file_sys/card_image.h" | ||||
| #include "core/file_sys/mode.h" | ||||
| #include "core/file_sys/registered_cache.h" | ||||
| #include "core/file_sys/romfs_factory.h" | ||||
| #include "core/file_sys/savedata_factory.h" | ||||
| #include "core/file_sys/sdmc_factory.h" | ||||
| #include "core/file_sys/vfs_concat.h" | ||||
| #include "core/file_sys/vfs_real.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| @ -27,6 +32,7 @@ | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/service/am/applets/applets.h" | ||||
| #include "core/hle/service/apm/controller.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/hle/service/glue/manager.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/hle/service/sm/sm.h" | ||||
| @ -202,6 +208,15 @@ struct System::Impl { | ||||
|         main_process->Run(load_parameters->main_thread_priority, | ||||
|                           load_parameters->main_thread_stack_size); | ||||
| 
 | ||||
|         if (Settings::values.gamecard_inserted) { | ||||
|             if (Settings::values.gamecard_current_game) { | ||||
|                 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); | ||||
|             } else if (!Settings::values.gamecard_path.empty()) { | ||||
|                 fs_controller.SetGameCard( | ||||
|                     GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         u64 title_id{0}; | ||||
|         if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { | ||||
|             LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", | ||||
| @ -304,6 +319,7 @@ struct System::Impl { | ||||
|     FileSys::VirtualFilesystem virtual_filesystem; | ||||
|     /// ContentProviderUnion instance
 | ||||
|     std::unique_ptr<FileSys::ContentProviderUnion> content_provider; | ||||
|     Service::FileSystem::FileSystemController fs_controller; | ||||
|     /// AppLoader used to load the current executing application
 | ||||
|     std::unique_ptr<Loader::AppLoader> app_loader; | ||||
|     std::unique_ptr<VideoCore::RendererBase> renderer; | ||||
| @ -571,6 +587,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const { | ||||
|     return *impl->content_provider; | ||||
| } | ||||
| 
 | ||||
| Service::FileSystem::FileSystemController& System::GetFileSystemController() { | ||||
|     return impl->fs_controller; | ||||
| } | ||||
| 
 | ||||
| const Service::FileSystem::FileSystemController& System::GetFileSystemController() const { | ||||
|     return impl->fs_controller; | ||||
| } | ||||
| 
 | ||||
| void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, | ||||
|                                      FileSys::ContentProvider* provider) { | ||||
|     impl->content_provider->SetSlot(slot, provider); | ||||
|  | ||||
| @ -47,6 +47,10 @@ namespace APM { | ||||
| class Controller; | ||||
| } | ||||
| 
 | ||||
| namespace FileSystem { | ||||
| class FileSystemController; | ||||
| } // namespace FileSystem
 | ||||
| 
 | ||||
| namespace Glue { | ||||
| class ARPManager; | ||||
| } | ||||
| @ -299,6 +303,10 @@ public: | ||||
| 
 | ||||
|     const FileSys::ContentProvider& GetContentProvider() const; | ||||
| 
 | ||||
|     Service::FileSystem::FileSystemController& GetFileSystemController(); | ||||
| 
 | ||||
|     const Service::FileSystem::FileSystemController& GetFileSystemController() const; | ||||
| 
 | ||||
|     void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, | ||||
|                                  FileSys::ContentProvider* provider); | ||||
| 
 | ||||
|  | ||||
| @ -480,6 +480,10 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) { | ||||
|     prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const { | ||||
|     return prodinfo_decrypted; | ||||
| } | ||||
| 
 | ||||
| std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const { | ||||
|     std::array<u8, 0x240> out{}; | ||||
|     if (prodinfo_decrypted != nullptr) | ||||
|  | ||||
| @ -84,6 +84,7 @@ public: | ||||
|     bool HasProdInfo() const; | ||||
|     FileSys::VirtualFile GetProdInfoRaw() const; | ||||
|     void DecryptProdInfo(std::array<u8, 0x20> bis_key); | ||||
|     FileSys::VirtualFile GetDecryptedProdInfo() const; | ||||
|     std::array<u8, 0x240> GetETicketExtendedKek() const; | ||||
| 
 | ||||
| private: | ||||
|  | ||||
| @ -3,8 +3,12 @@ | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| #include "common/file_util.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/bis_factory.h" | ||||
| #include "core/file_sys/mode.h" | ||||
| #include "core/file_sys/registered_cache.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| @ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir | ||||
|       sysnand_cache(std::make_unique<RegisteredCache>( | ||||
|           GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), | ||||
|       usrnand_cache(std::make_unique<RegisteredCache>( | ||||
|           GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} | ||||
|           GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))), | ||||
|       sysnand_placeholder(std::make_unique<PlaceholderCache>( | ||||
|           GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))), | ||||
|       usrnand_placeholder(std::make_unique<PlaceholderCache>( | ||||
|           GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {} | ||||
| 
 | ||||
| BISFactory::~BISFactory() = default; | ||||
| 
 | ||||
| VirtualDir BISFactory::GetSystemNANDContentDirectory() const { | ||||
|     return GetOrCreateDirectoryRelative(nand_root, "/system/Contents"); | ||||
| } | ||||
| 
 | ||||
| VirtualDir BISFactory::GetUserNANDContentDirectory() const { | ||||
|     return GetOrCreateDirectoryRelative(nand_root, "/user/Contents"); | ||||
| } | ||||
| 
 | ||||
| RegisteredCache* BISFactory::GetSystemNANDContents() const { | ||||
|     return sysnand_cache.get(); | ||||
| } | ||||
| @ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const { | ||||
|     return usrnand_cache.get(); | ||||
| } | ||||
| 
 | ||||
| PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const { | ||||
|     return sysnand_placeholder.get(); | ||||
| } | ||||
| 
 | ||||
| PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const { | ||||
|     return usrnand_placeholder.get(); | ||||
| } | ||||
| 
 | ||||
| VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { | ||||
|     // LayeredFS doesn't work on updates and title id-less homebrew
 | ||||
|     if (title_id == 0 || (title_id & 0x800) > 0) | ||||
|     if (title_id == 0 || (title_id & 0xFFF) == 0x800) | ||||
|         return nullptr; | ||||
|     return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); | ||||
| } | ||||
| @ -39,4 +63,77 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const { | ||||
|     return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); | ||||
| } | ||||
| 
 | ||||
| VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { | ||||
|     switch (id) { | ||||
|     case BisPartitionId::CalibrationFile: | ||||
|         return GetOrCreateDirectoryRelative(nand_root, "/prodinfof"); | ||||
|     case BisPartitionId::SafeMode: | ||||
|         return GetOrCreateDirectoryRelative(nand_root, "/safe"); | ||||
|     case BisPartitionId::System: | ||||
|         return GetOrCreateDirectoryRelative(nand_root, "/system"); | ||||
|     case BisPartitionId::User: | ||||
|         return GetOrCreateDirectoryRelative(nand_root, "/user"); | ||||
|     default: | ||||
|         return nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { | ||||
|     Core::Crypto::KeyManager keys; | ||||
|     Core::Crypto::PartitionDataManager pdm{ | ||||
|         Core::System::GetInstance().GetFilesystem()->OpenDirectory( | ||||
|             FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; | ||||
|     keys.PopulateFromPartitionData(pdm); | ||||
| 
 | ||||
|     switch (id) { | ||||
|     case BisPartitionId::CalibrationBinary: | ||||
|         return pdm.GetDecryptedProdInfo(); | ||||
|     case BisPartitionId::BootConfigAndPackage2Part1: | ||||
|     case BisPartitionId::BootConfigAndPackage2Part2: | ||||
|     case BisPartitionId::BootConfigAndPackage2Part3: | ||||
|     case BisPartitionId::BootConfigAndPackage2Part4: | ||||
|     case BisPartitionId::BootConfigAndPackage2Part5: | ||||
|     case BisPartitionId::BootConfigAndPackage2Part6: { | ||||
|         const auto new_id = static_cast<u8>(id) - | ||||
|                             static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) + | ||||
|                             static_cast<u8>(Core::Crypto::Package2Type::NormalMain); | ||||
|         return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id)); | ||||
|     } | ||||
|     default: | ||||
|         return nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| VirtualDir BISFactory::GetImageDirectory() const { | ||||
|     return GetOrCreateDirectoryRelative(nand_root, "/user/Album"); | ||||
| } | ||||
| 
 | ||||
| u64 BISFactory::GetSystemNANDFreeSpace() const { | ||||
|     const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system"); | ||||
|     if (sys_dir == nullptr) | ||||
|         return 0; | ||||
| 
 | ||||
|     return GetSystemNANDTotalSpace() - sys_dir->GetSize(); | ||||
| } | ||||
| 
 | ||||
| u64 BISFactory::GetSystemNANDTotalSpace() const { | ||||
|     return static_cast<u64>(Settings::values.nand_system_size); | ||||
| } | ||||
| 
 | ||||
| u64 BISFactory::GetUserNANDFreeSpace() const { | ||||
|     const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user"); | ||||
|     if (usr_dir == nullptr) | ||||
|         return 0; | ||||
| 
 | ||||
|     return GetUserNANDTotalSpace() - usr_dir->GetSize(); | ||||
| } | ||||
| 
 | ||||
| u64 BISFactory::GetUserNANDTotalSpace() const { | ||||
|     return static_cast<u64>(Settings::values.nand_user_size); | ||||
| } | ||||
| 
 | ||||
| u64 BISFactory::GetFullNANDTotalSpace() const { | ||||
|     return static_cast<u64>(Settings::values.nand_total_size); | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | ||||
| @ -10,7 +10,25 @@ | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| enum class BisPartitionId : u32 { | ||||
|     UserDataRoot = 20, | ||||
|     CalibrationBinary = 27, | ||||
|     CalibrationFile = 28, | ||||
|     BootConfigAndPackage2Part1 = 21, | ||||
|     BootConfigAndPackage2Part2 = 22, | ||||
|     BootConfigAndPackage2Part3 = 23, | ||||
|     BootConfigAndPackage2Part4 = 24, | ||||
|     BootConfigAndPackage2Part5 = 25, | ||||
|     BootConfigAndPackage2Part6 = 26, | ||||
|     SafeMode = 29, | ||||
|     System = 31, | ||||
|     SystemProperEncryption = 32, | ||||
|     SystemProperPartition = 33, | ||||
|     User = 30, | ||||
| }; | ||||
| 
 | ||||
| class RegisteredCache; | ||||
| class PlaceholderCache; | ||||
| 
 | ||||
| /// File system interface to the Built-In Storage
 | ||||
| /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
 | ||||
| @ -20,12 +38,29 @@ public: | ||||
|     explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); | ||||
|     ~BISFactory(); | ||||
| 
 | ||||
|     VirtualDir GetSystemNANDContentDirectory() const; | ||||
|     VirtualDir GetUserNANDContentDirectory() const; | ||||
| 
 | ||||
|     RegisteredCache* GetSystemNANDContents() const; | ||||
|     RegisteredCache* GetUserNANDContents() const; | ||||
| 
 | ||||
|     PlaceholderCache* GetSystemNANDPlaceholder() const; | ||||
|     PlaceholderCache* GetUserNANDPlaceholder() const; | ||||
| 
 | ||||
|     VirtualDir GetModificationLoadRoot(u64 title_id) const; | ||||
|     VirtualDir GetModificationDumpRoot(u64 title_id) const; | ||||
| 
 | ||||
|     VirtualDir OpenPartition(BisPartitionId id) const; | ||||
|     VirtualFile OpenPartitionStorage(BisPartitionId id) const; | ||||
| 
 | ||||
|     VirtualDir GetImageDirectory() const; | ||||
| 
 | ||||
|     u64 GetSystemNANDFreeSpace() const; | ||||
|     u64 GetSystemNANDTotalSpace() const; | ||||
|     u64 GetUserNANDFreeSpace() const; | ||||
|     u64 GetUserNANDTotalSpace() const; | ||||
|     u64 GetFullNANDTotalSpace() const; | ||||
| 
 | ||||
| private: | ||||
|     VirtualDir nand_root; | ||||
|     VirtualDir load_root; | ||||
| @ -33,6 +68,9 @@ private: | ||||
| 
 | ||||
|     std::unique_ptr<RegisteredCache> sysnand_cache; | ||||
|     std::unique_ptr<RegisteredCache> usrnand_cache; | ||||
| 
 | ||||
|     std::unique_ptr<PlaceholderCache> sysnand_placeholder; | ||||
|     std::unique_ptr<PlaceholderCache> usrnand_placeholder; | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | ||||
| @ -12,12 +12,16 @@ | ||||
| #include "core/file_sys/content_archive.h" | ||||
| #include "core/file_sys/nca_metadata.h" | ||||
| #include "core/file_sys/partition_filesystem.h" | ||||
| #include "core/file_sys/romfs.h" | ||||
| #include "core/file_sys/submission_package.h" | ||||
| #include "core/file_sys/vfs_concat.h" | ||||
| #include "core/file_sys/vfs_offset.h" | ||||
| #include "core/file_sys/vfs_vector.h" | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000; | ||||
| constexpr std::array partition_names{ | ||||
|     "update", | ||||
|     "normal", | ||||
| @ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const { | ||||
|     return file->GetContainingDirectory(); | ||||
| } | ||||
| 
 | ||||
| VirtualDir XCI::ConcatenatedPseudoDirectory() { | ||||
|     const auto out = std::make_shared<VectorVfsDirectory>(); | ||||
|     for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) { | ||||
|         const auto& part = GetPartition(part_id); | ||||
|         if (part == nullptr) | ||||
|             continue; | ||||
| 
 | ||||
|         for (const auto& file : part->GetFiles()) | ||||
|             out->AddFile(file); | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| std::array<u8, 0x200> XCI::GetCertificate() const { | ||||
|     std::array<u8, 0x200> out; | ||||
|     file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | ||||
|     const auto partition_index = static_cast<std::size_t>(part); | ||||
|     const auto& partition = partitions[partition_index]; | ||||
|  | ||||
| @ -91,6 +91,8 @@ public: | ||||
|     VirtualDir GetLogoPartition() const; | ||||
| 
 | ||||
|     u64 GetProgramTitleID() const; | ||||
|     u32 GetSystemUpdateVersion(); | ||||
|     u64 GetSystemUpdateTitleID() const; | ||||
| 
 | ||||
|     bool HasProgramNCA() const; | ||||
|     VirtualFile GetProgramNCAFile() const; | ||||
| @ -106,6 +108,11 @@ public: | ||||
| 
 | ||||
|     VirtualDir GetParentDirectory() const override; | ||||
| 
 | ||||
|     // Creates a directory that contains all the NCAs in the gamecard
 | ||||
|     VirtualDir ConcatenatedPseudoDirectory(); | ||||
| 
 | ||||
|     std::array<u8, 0x200> GetCertificate() const; | ||||
| 
 | ||||
| private: | ||||
|     Loader::ResultStatus AddNCAFromPartition(XCIPartition part); | ||||
| 
 | ||||
| @ -120,6 +127,8 @@ private: | ||||
|     std::shared_ptr<NCA> program; | ||||
|     std::vector<std::shared_ptr<NCA>> ncas; | ||||
| 
 | ||||
|     u64 update_normal_partition_end; | ||||
| 
 | ||||
|     Core::Crypto::KeyManager keys; | ||||
| }; | ||||
| } // namespace FileSys
 | ||||
|  | ||||
| @ -528,6 +528,14 @@ u64 NCA::GetTitleId() const { | ||||
|     return header.title_id; | ||||
| } | ||||
| 
 | ||||
| std::array<u8, 16> NCA::GetRightsId() const { | ||||
|     return header.rights_id; | ||||
| } | ||||
| 
 | ||||
| u32 NCA::GetSDKVersion() const { | ||||
|     return header.sdk_version; | ||||
| } | ||||
| 
 | ||||
| bool NCA::IsUpdate() const { | ||||
|     return is_update; | ||||
| } | ||||
|  | ||||
| @ -112,6 +112,8 @@ public: | ||||
| 
 | ||||
|     NCAContentType GetType() const; | ||||
|     u64 GetTitleId() const; | ||||
|     std::array<u8, 0x10> GetRightsId() const; | ||||
|     u32 GetSDKVersion() const; | ||||
|     bool IsUpdate() const; | ||||
| 
 | ||||
|     VirtualFile GetRomFS() const; | ||||
|  | ||||
| @ -63,7 +63,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | ||||
| 
 | ||||
|     if (Settings::values.dump_exefs) { | ||||
|         LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); | ||||
|         const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | ||||
|         const auto dump_dir = | ||||
|             Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); | ||||
|         if (dump_dir != nullptr) { | ||||
|             const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); | ||||
|             VfsRawCopyD(exefs, exefs_dir); | ||||
| @ -88,7 +89,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | ||||
|     } | ||||
| 
 | ||||
|     // LayeredExeFS
 | ||||
|     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||||
|     const auto load_dir = | ||||
|         Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||||
|     if (load_dir != nullptr && load_dir->GetSize() > 0) { | ||||
|         auto patch_dirs = load_dir->GetSubdirectories(); | ||||
|         std::sort( | ||||
| @ -174,7 +176,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | ||||
|     if (Settings::values.dump_nso) { | ||||
|         LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, | ||||
|                  title_id); | ||||
|         const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | ||||
|         const auto dump_dir = | ||||
|             Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); | ||||
|         if (dump_dir != nullptr) { | ||||
|             const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); | ||||
|             const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); | ||||
| @ -186,7 +189,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | ||||
| 
 | ||||
|     LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); | ||||
| 
 | ||||
|     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||||
|     const auto load_dir = | ||||
|         Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||||
|     if (load_dir == nullptr) { | ||||
|         LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||||
|         return nso; | ||||
|     } | ||||
| 
 | ||||
|     auto patch_dirs = load_dir->GetSubdirectories(); | ||||
|     std::sort(patch_dirs.begin(), patch_dirs.end(), | ||||
|               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||||
| @ -224,7 +233,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | ||||
| 
 | ||||
|     LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); | ||||
| 
 | ||||
|     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||||
|     const auto load_dir = | ||||
|         Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||||
|     if (load_dir == nullptr) { | ||||
|         LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     auto patch_dirs = load_dir->GetSubdirectories(); | ||||
|     std::sort(patch_dirs.begin(), patch_dirs.end(), | ||||
|               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||||
| @ -258,7 +273,13 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst | ||||
| 
 | ||||
| std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, | ||||
|                                                      const std::array<u8, 32>& build_id_) const { | ||||
|     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||||
|     const auto load_dir = | ||||
|         Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||||
|     if (load_dir == nullptr) { | ||||
|         LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     auto patch_dirs = load_dir->GetSubdirectories(); | ||||
|     std::sort(patch_dirs.begin(), patch_dirs.end(), | ||||
|               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||||
| @ -284,7 +305,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, | ||||
| } | ||||
| 
 | ||||
| static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | ||||
|     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||||
|     const auto load_dir = | ||||
|         Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||||
|     if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || | ||||
|         load_dir == nullptr || load_dir->GetSize() <= 0) { | ||||
|         return; | ||||
| @ -393,6 +415,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||||
| 
 | ||||
| std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( | ||||
|     VirtualFile update_raw) const { | ||||
|     if (title_id == 0) | ||||
|         return {}; | ||||
|     std::map<std::string, std::string, std::less<>> out; | ||||
|     const auto& installed = Core::System::GetInstance().GetContentProvider(); | ||||
|     const auto& disabled = Settings::values.disabled_addons[title_id]; | ||||
| @ -423,7 +447,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | ||||
|     } | ||||
| 
 | ||||
|     // General Mods (LayeredFS and IPS)
 | ||||
|     const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||||
|     const auto mod_dir = | ||||
|         Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||||
|     if (mod_dir != nullptr && mod_dir->GetSize() > 0) { | ||||
|         for (const auto& mod : mod_dir->GetSubdirectories()) { | ||||
|             std::string types; | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <random> | ||||
| #include <regex> | ||||
| #include <mbedtls/sha256.h> | ||||
| #include "common/assert.h" | ||||
| @ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) { | ||||
| static bool FollowsNcaIdFormat(std::string_view name) { | ||||
|     static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | | ||||
|                                                                    std::regex_constants::icase); | ||||
|     return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); | ||||
|     static const std::regex nca_id_cnmt_regex( | ||||
|         "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase); | ||||
|     return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) || | ||||
|            (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex)); | ||||
| } | ||||
| 
 | ||||
| static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, | ||||
|                                             bool within_two_digit) { | ||||
|     if (!within_two_digit) { | ||||
|         return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper)); | ||||
|     } | ||||
|                                             bool within_two_digit, bool cnmt_suffix) { | ||||
|     if (!within_two_digit) | ||||
|         return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca", | ||||
|                            Common::HexToString(nca_id, second_hex_upper)); | ||||
| 
 | ||||
|     Core::Crypto::SHA256Hash hash{}; | ||||
|     mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); | ||||
|     return fmt::format("/000000{:02X}/{}.nca", hash[0], | ||||
|     return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0], | ||||
|                        Common::HexToString(nca_id, second_hex_upper)); | ||||
| } | ||||
| 
 | ||||
| @ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const { | ||||
|     return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); | ||||
| } | ||||
| 
 | ||||
| PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {} | ||||
| 
 | ||||
| bool PlaceholderCache::Create(const NcaID& id, u64 size) const { | ||||
|     const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
| 
 | ||||
|     if (dir->GetFileRelative(path) != nullptr) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     Core::Crypto::SHA256Hash hash{}; | ||||
|     mbedtls_sha256(id.data(), id.size(), hash.data(), 0); | ||||
|     const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||||
| 
 | ||||
|     const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||||
| 
 | ||||
|     if (dir2 == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||||
| 
 | ||||
|     if (file == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     return file->Resize(size); | ||||
| } | ||||
| 
 | ||||
| bool PlaceholderCache::Delete(const NcaID& id) const { | ||||
|     const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
| 
 | ||||
|     if (dir->GetFileRelative(path) == nullptr) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     Core::Crypto::SHA256Hash hash{}; | ||||
|     mbedtls_sha256(id.data(), id.size(), hash.data(), 0); | ||||
|     const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||||
| 
 | ||||
|     const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||||
| 
 | ||||
|     const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||||
| 
 | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| bool PlaceholderCache::Exists(const NcaID& id) const { | ||||
|     const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
| 
 | ||||
|     return dir->GetFileRelative(path) != nullptr; | ||||
| } | ||||
| 
 | ||||
| bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const { | ||||
|     const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
|     const auto file = dir->GetFileRelative(path); | ||||
| 
 | ||||
|     if (file == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     return file->WriteBytes(data, offset) == data.size(); | ||||
| } | ||||
| 
 | ||||
| bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder, | ||||
|                                 const NcaID& install) const { | ||||
|     const auto path = GetRelativePathFromNcaID(placeholder, false, true, false); | ||||
|     const auto file = dir->GetFileRelative(path); | ||||
| 
 | ||||
|     if (file == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install); | ||||
| 
 | ||||
|     if (res != InstallResult::Success) | ||||
|         return false; | ||||
| 
 | ||||
|     return Delete(placeholder); | ||||
| } | ||||
| 
 | ||||
| bool PlaceholderCache::CleanAll() const { | ||||
|     return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName()); | ||||
| } | ||||
| 
 | ||||
| std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const { | ||||
|     const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
|     const auto file = dir->GetFileRelative(path); | ||||
| 
 | ||||
|     if (file == nullptr) | ||||
|         return std::nullopt; | ||||
| 
 | ||||
|     NCA nca{file}; | ||||
| 
 | ||||
|     if (nca.GetStatus() != Loader::ResultStatus::Success && | ||||
|         nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     const auto rights_id = nca.GetRightsId(); | ||||
|     if (rights_id == NcaID{}) | ||||
|         return std::nullopt; | ||||
| 
 | ||||
|     return rights_id; | ||||
| } | ||||
| 
 | ||||
| u64 PlaceholderCache::Size(const NcaID& id) const { | ||||
|     const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
|     const auto file = dir->GetFileRelative(path); | ||||
| 
 | ||||
|     if (file == nullptr) | ||||
|         return 0; | ||||
| 
 | ||||
|     return file->GetSize(); | ||||
| } | ||||
| 
 | ||||
| bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const { | ||||
|     const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
|     const auto file = dir->GetFileRelative(path); | ||||
| 
 | ||||
|     if (file == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     return file->Resize(new_size); | ||||
| } | ||||
| 
 | ||||
| std::vector<NcaID> PlaceholderCache::List() const { | ||||
|     std::vector<NcaID> out; | ||||
|     for (const auto& sdir : dir->GetSubdirectories()) { | ||||
|         for (const auto& file : sdir->GetFiles()) { | ||||
|             const auto name = file->GetName(); | ||||
|             if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && | ||||
|                 name[35] == 'a') { | ||||
|                 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| NcaID PlaceholderCache::Generate() { | ||||
|     std::random_device device; | ||||
|     std::mt19937 gen(device()); | ||||
|     std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | ||||
| 
 | ||||
|     NcaID out{}; | ||||
| 
 | ||||
|     const auto v1 = distribution(gen); | ||||
|     const auto v2 = distribution(gen); | ||||
|     std::memcpy(out.data(), &v1, sizeof(u64)); | ||||
|     std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64)); | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | ||||
|                                                        std::string_view path) const { | ||||
|     const auto file = dir->GetFileRelative(path); | ||||
| @ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | ||||
| 
 | ||||
| VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { | ||||
|     VirtualFile file; | ||||
|     // Try all four modes of file storage:
 | ||||
|     // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
 | ||||
|     // 00: /000000**/{:032X}.nca
 | ||||
|     // 01: /{:032X}.nca
 | ||||
|     // 10: /000000**/{:032x}.nca
 | ||||
|     // 11: /{:032x}.nca
 | ||||
|     for (u8 i = 0; i < 4; ++i) { | ||||
|         const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0); | ||||
|     // Try all five relevant modes of file storage:
 | ||||
|     // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
 | ||||
|     // 000: /000000**/{:032X}.nca
 | ||||
|     // 010: /{:032X}.nca
 | ||||
|     // 100: /000000**/{:032x}.nca
 | ||||
|     // 110: /{:032x}.nca
 | ||||
|     // 111: /{:032x}.cnmt.nca
 | ||||
|     for (u8 i = 0; i < 8; ++i) { | ||||
|         if ((i % 2) == 1 && i != 7) | ||||
|             continue; | ||||
|         const auto path = | ||||
|             GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001); | ||||
|         file = OpenFileOrDirectoryConcat(dir, path); | ||||
|         if (file != nullptr) | ||||
|             return file; | ||||
| @ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti | ||||
|         memcpy(id.data(), hash.data(), 16); | ||||
|     } | ||||
| 
 | ||||
|     std::string path = GetRelativePathFromNcaID(id, false, true); | ||||
|     std::string path = GetRelativePathFromNcaID(id, false, true, false); | ||||
| 
 | ||||
|     if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { | ||||
|         LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); | ||||
|  | ||||
| @ -25,6 +25,8 @@ enum class NCAContentType : u8; | ||||
| enum class TitleType : u8; | ||||
| 
 | ||||
| struct ContentRecord; | ||||
| struct MetaRecord; | ||||
| class RegisteredCache; | ||||
| 
 | ||||
| using NcaID = std::array<u8, 0x10>; | ||||
| using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; | ||||
| @ -89,6 +91,27 @@ protected: | ||||
|     Core::Crypto::KeyManager keys; | ||||
| }; | ||||
| 
 | ||||
| class PlaceholderCache { | ||||
| public: | ||||
|     explicit PlaceholderCache(VirtualDir dir); | ||||
| 
 | ||||
|     bool Create(const NcaID& id, u64 size) const; | ||||
|     bool Delete(const NcaID& id) const; | ||||
|     bool Exists(const NcaID& id) const; | ||||
|     bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const; | ||||
|     bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const; | ||||
|     bool CleanAll() const; | ||||
|     std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const; | ||||
|     u64 Size(const NcaID& id) const; | ||||
|     bool SetSize(const NcaID& id, u64 new_size) const; | ||||
|     std::vector<NcaID> List() const; | ||||
| 
 | ||||
|     static NcaID Generate(); | ||||
| 
 | ||||
| private: | ||||
|     VirtualDir dir; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * A class that catalogues NCAs in the registered directory structure. | ||||
|  * Nintendo's registered format follows this structure: | ||||
| @ -103,6 +126,8 @@ protected: | ||||
|  * when 4GB splitting can be ignored.) | ||||
|  */ | ||||
| class RegisteredCache : public ContentProvider { | ||||
|     friend class PlaceholderCache; | ||||
| 
 | ||||
| public: | ||||
|     // Parsing function defines the conversion from raw file to NCA. If there are other steps
 | ||||
|     // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
 | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/card_image.h" | ||||
| #include "core/file_sys/content_archive.h" | ||||
| #include "core/file_sys/nca_metadata.h" | ||||
| #include "core/file_sys/patch_manager.h" | ||||
| @ -34,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) { | ||||
|     this->update_raw = std::move(update_raw); | ||||
| } | ||||
| 
 | ||||
| ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | ||||
| ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const { | ||||
|     if (!updatable) | ||||
|         return MakeResult<VirtualFile>(file); | ||||
| 
 | ||||
| @ -43,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | ||||
|         patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { | ||||
| ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, | ||||
|                                           ContentRecordType type) const { | ||||
|     std::shared_ptr<NCA> res; | ||||
| 
 | ||||
|     switch (storage) { | ||||
| @ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte | ||||
|         res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); | ||||
|         break; | ||||
|     case StorageId::NandSystem: | ||||
|         res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); | ||||
|         res = | ||||
|             Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry( | ||||
|                 title_id, type); | ||||
|         break; | ||||
|     case StorageId::NandUser: | ||||
|         res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type); | ||||
|         res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry( | ||||
|             title_id, type); | ||||
|         break; | ||||
|     case StorageId::SdCard: | ||||
|         res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type); | ||||
|         res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry( | ||||
|             title_id, type); | ||||
|         break; | ||||
|     default: | ||||
|         UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); | ||||
|  | ||||
| @ -33,8 +33,8 @@ public: | ||||
|     ~RomFSFactory(); | ||||
| 
 | ||||
|     void SetPackedUpdate(VirtualFile update_raw); | ||||
|     ResultVal<VirtualFile> OpenCurrentProcess(); | ||||
|     ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); | ||||
|     ResultVal<VirtualFile> OpenCurrentProcess() const; | ||||
|     ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; | ||||
| 
 | ||||
| private: | ||||
|     VirtualFile file; | ||||
|  | ||||
| @ -15,22 +15,8 @@ namespace FileSys { | ||||
| 
 | ||||
| constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; | ||||
| 
 | ||||
| std::string SaveDataDescriptor::DebugInfo() const { | ||||
|     return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, " | ||||
|                        "rank={}, index={}]", | ||||
|                        static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | ||||
|                        static_cast<u8>(rank), index); | ||||
| } | ||||
| 
 | ||||
| SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { | ||||
|     // Delete all temporary storages
 | ||||
|     // On hardware, it is expected that temporary storage be empty at first use.
 | ||||
|     dir->DeleteSubdirectoryRecursive("temp"); | ||||
| } | ||||
| 
 | ||||
| SaveDataFactory::~SaveDataFactory() = default; | ||||
| 
 | ||||
| ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) { | ||||
| namespace { | ||||
| void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { | ||||
|     if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | ||||
|         if (meta.zero_1 != 0) { | ||||
|             LOG_WARNING(Service_FS, | ||||
| @ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat | ||||
|                     "non-zero ({:016X}{:016X})", | ||||
|                     meta.user_id[1], meta.user_id[0]); | ||||
|     } | ||||
| } | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
|     std::string save_directory = | ||||
| std::string SaveDataDescriptor::DebugInfo() const { | ||||
|     return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, " | ||||
|                        "save_id={:016X}, " | ||||
|                        "rank={}, index={}]", | ||||
|                        static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | ||||
|                        static_cast<u8>(rank), index); | ||||
| } | ||||
| 
 | ||||
| SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { | ||||
|     // Delete all temporary storages
 | ||||
|     // On hardware, it is expected that temporary storage be empty at first use.
 | ||||
|     dir->DeleteSubdirectoryRecursive("temp"); | ||||
| } | ||||
| 
 | ||||
| SaveDataFactory::~SaveDataFactory() = default; | ||||
| 
 | ||||
| ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, | ||||
|                                               const SaveDataDescriptor& meta) const { | ||||
|     PrintSaveDataDescriptorWarnings(meta); | ||||
| 
 | ||||
|     const auto save_directory = | ||||
|         GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | ||||
| 
 | ||||
|     // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
 | ||||
|     // But, user_ids don't match so this works for now.
 | ||||
|     auto out = dir->CreateDirectoryRelative(save_directory); | ||||
| 
 | ||||
|     // Return an error if the save data doesn't actually exist.
 | ||||
|     if (out == nullptr) { | ||||
|         // TODO(DarkLordZach): Find out correct error code.
 | ||||
|         return ResultCode(-1); | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<VirtualDir>(std::move(out)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, | ||||
|                                             const SaveDataDescriptor& meta) const { | ||||
| 
 | ||||
|     const auto save_directory = | ||||
|         GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | ||||
| 
 | ||||
|     auto out = dir->GetDirectoryRelative(save_directory); | ||||
| 
 | ||||
|     if (out == nullptr) { | ||||
|         // TODO(bunnei): This is a work-around to always create a save data directory if it does not
 | ||||
|         // already exist. This is a hack, as we do not understand yet how this works on hardware.
 | ||||
|         // Without a save data directory, many games will assert on boot. This should not have any
 | ||||
|         // bad side-effects.
 | ||||
|         out = dir->CreateDirectoryRelative(save_directory); | ||||
|     } | ||||
| 
 | ||||
|     // Return an error if the save data doesn't actually exist.
 | ||||
|     if (out == nullptr) { | ||||
|         // TODO(Subv): Find out correct error code.
 | ||||
| @ -152,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, | ||||
| } | ||||
| 
 | ||||
| void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, | ||||
|                                         SaveDataSize new_value) { | ||||
|                                         SaveDataSize new_value) const { | ||||
|     const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); | ||||
|     const auto dir = GetOrCreateDirectoryRelative(this->dir, path); | ||||
| 
 | ||||
|  | ||||
| @ -64,7 +64,8 @@ public: | ||||
|     explicit SaveDataFactory(VirtualDir dir); | ||||
|     ~SaveDataFactory(); | ||||
| 
 | ||||
|     ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta); | ||||
|     ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | ||||
|     ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | ||||
| 
 | ||||
|     VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; | ||||
| 
 | ||||
| @ -73,7 +74,8 @@ public: | ||||
|                                    u128 user_id, u64 save_id); | ||||
| 
 | ||||
|     SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; | ||||
|     void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value); | ||||
|     void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, | ||||
|                            SaveDataSize new_value) const; | ||||
| 
 | ||||
| private: | ||||
|     VirtualDir dir; | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| #include "core/file_sys/registered_cache.h" | ||||
| #include "core/file_sys/sdmc_factory.h" | ||||
| #include "core/file_sys/xts_archive.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| @ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_) | ||||
|                                 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), | ||||
|                                 [](const VirtualFile& file, const NcaID& id) { | ||||
|                                     return NAX{file, id}.GetDecrypted(); | ||||
|                                 })) {} | ||||
|                                 })), | ||||
|       placeholder(std::make_unique<PlaceholderCache>( | ||||
|           GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {} | ||||
| 
 | ||||
| SDMCFactory::~SDMCFactory() = default; | ||||
| 
 | ||||
| ResultVal<VirtualDir> SDMCFactory::Open() { | ||||
| ResultVal<VirtualDir> SDMCFactory::Open() const { | ||||
|     return MakeResult<VirtualDir>(dir); | ||||
| } | ||||
| 
 | ||||
| VirtualDir SDMCFactory::GetSDMCContentDirectory() const { | ||||
|     return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents"); | ||||
| } | ||||
| 
 | ||||
| RegisteredCache* SDMCFactory::GetSDMCContents() const { | ||||
|     return contents.get(); | ||||
| } | ||||
| 
 | ||||
| PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const { | ||||
|     return placeholder.get(); | ||||
| } | ||||
| 
 | ||||
| VirtualDir SDMCFactory::GetImageDirectory() const { | ||||
|     return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album"); | ||||
| } | ||||
| 
 | ||||
| u64 SDMCFactory::GetSDMCFreeSpace() const { | ||||
|     return GetSDMCTotalSpace() - dir->GetSize(); | ||||
| } | ||||
| 
 | ||||
| u64 SDMCFactory::GetSDMCTotalSpace() const { | ||||
|     return static_cast<u64>(Settings::values.sdmc_size); | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | ||||
| @ -11,6 +11,7 @@ | ||||
| namespace FileSys { | ||||
| 
 | ||||
| class RegisteredCache; | ||||
| class PlaceholderCache; | ||||
| 
 | ||||
| /// File system interface to the SDCard archive
 | ||||
| class SDMCFactory { | ||||
| @ -18,13 +19,23 @@ public: | ||||
|     explicit SDMCFactory(VirtualDir dir); | ||||
|     ~SDMCFactory(); | ||||
| 
 | ||||
|     ResultVal<VirtualDir> Open(); | ||||
|     ResultVal<VirtualDir> Open() const; | ||||
| 
 | ||||
|     VirtualDir GetSDMCContentDirectory() const; | ||||
| 
 | ||||
|     RegisteredCache* GetSDMCContents() const; | ||||
|     PlaceholderCache* GetSDMCPlaceholder() const; | ||||
| 
 | ||||
|     VirtualDir GetImageDirectory() const; | ||||
| 
 | ||||
|     u64 GetSDMCFreeSpace() const; | ||||
|     u64 GetSDMCTotalSpace() const; | ||||
| 
 | ||||
| private: | ||||
|     VirtualDir dir; | ||||
| 
 | ||||
|     std::unique_ptr<RegisteredCache> contents; | ||||
|     std::unique_ptr<PlaceholderCache> placeholder; | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | ||||
| @ -248,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { | ||||
| 
 | ||||
| void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | ||||
|     for (const auto& outer_file : files) { | ||||
|         if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | ||||
|         if (outer_file->GetName().size() < 9 || | ||||
|             outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -1143,13 +1143,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( | ||||
| 
 | ||||
| void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     u128 uid = rp.PopRaw<u128>(); // What does this do?
 | ||||
|     LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); | ||||
|     u128 user_id = rp.PopRaw<u128>(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); | ||||
| 
 | ||||
|     FileSys::SaveDataDescriptor descriptor{}; | ||||
|     descriptor.title_id = Core::CurrentProcess()->GetTitleID(); | ||||
|     descriptor.user_id = user_id; | ||||
|     descriptor.type = FileSys::SaveDataType::SaveData; | ||||
|     const auto res = system.GetFileSystemController().CreateSaveData( | ||||
|         FileSys::SaveDataSpaceId::NandUser, descriptor); | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 4}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(res.Code()); | ||||
|     rb.Push<u64>(0); | ||||
| } // namespace Service::AM
 | ||||
| } | ||||
| 
 | ||||
| void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { | ||||
|     // Takes an input u32 Result, no output.
 | ||||
| @ -1261,8 +1269,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) { | ||||
|               "new_journal={:016X}", | ||||
|               static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); | ||||
| 
 | ||||
|     const auto title_id = system.CurrentProcess()->GetTitleID(); | ||||
|     FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size}); | ||||
|     system.GetFileSystemController().WriteSaveDataSize( | ||||
|         type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size}); | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 4}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| @ -1281,8 +1289,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), | ||||
|               user_id[1], user_id[0]); | ||||
| 
 | ||||
|     const auto title_id = system.CurrentProcess()->GetTitleID(); | ||||
|     const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id); | ||||
|     const auto size = system.GetFileSystemController().ReadSaveDataSize( | ||||
|         type, system.CurrentProcess()->GetTitleID(), user_id); | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 6}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|  | ||||
| @ -9,6 +9,10 @@ | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace FileSystem { | ||||
| class FileSystemController; | ||||
| } | ||||
| 
 | ||||
| namespace NVFlinger { | ||||
| class NVFlinger; | ||||
| } | ||||
|  | ||||
| @ -9,6 +9,10 @@ | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace FileSystem { | ||||
| class FileSystemController; | ||||
| } | ||||
| 
 | ||||
| namespace NVFlinger { | ||||
| class NVFlinger; | ||||
| } | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| #include "common/file_util.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/bis_factory.h" | ||||
| #include "core/file_sys/card_image.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| #include "core/file_sys/errors.h" | ||||
| #include "core/file_sys/mode.h" | ||||
| @ -25,14 +26,10 @@ | ||||
| #include "core/hle/service/filesystem/fsp_pr.h" | ||||
| #include "core/hle/service/filesystem/fsp_srv.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| namespace Service::FileSystem { | ||||
| 
 | ||||
| // Size of emulated sd card free space, reported in bytes.
 | ||||
| // Just using 32GB because thats reasonable
 | ||||
| // TODO(DarkLordZach): Eventually make this configurable in settings.
 | ||||
| constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; | ||||
| 
 | ||||
| // A default size for normal/journal save data size if application control metadata cannot be found.
 | ||||
| // This should be large enough to satisfy even the most extreme requirements (~4.2GB)
 | ||||
| constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; | ||||
| @ -226,13 +223,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s | ||||
|     return MakeResult(dir); | ||||
| } | ||||
| 
 | ||||
| u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const { | ||||
|     if (backing->IsWritable()) | ||||
|         return EMULATED_SD_REPORTED_SIZE; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | ||||
|     const std::string& path_) const { | ||||
|     std::string path(FileUtil::SanitizePath(path_)); | ||||
| @ -251,44 +241,39 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | ||||
|     return FileSys::ERROR_PATH_NOT_FOUND; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Map of registered file systems, identified by type. Once an file system is registered here, it | ||||
|  * is never removed until UnregisterFileSystems is called. | ||||
|  */ | ||||
| static std::unique_ptr<FileSys::RomFSFactory> romfs_factory; | ||||
| static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; | ||||
| static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; | ||||
| static std::unique_ptr<FileSys::BISFactory> bis_factory; | ||||
| FileSystemController::FileSystemController() = default; | ||||
| 
 | ||||
| ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { | ||||
|     ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS"); | ||||
| FileSystemController::~FileSystemController() = default; | ||||
| 
 | ||||
| ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { | ||||
|     romfs_factory = std::move(factory); | ||||
|     LOG_DEBUG(Service_FS, "Registered RomFS"); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { | ||||
|     ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data"); | ||||
| ResultCode FileSystemController::RegisterSaveData( | ||||
|     std::unique_ptr<FileSys::SaveDataFactory>&& factory) { | ||||
|     ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); | ||||
|     save_data_factory = std::move(factory); | ||||
|     LOG_DEBUG(Service_FS, "Registered save data"); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { | ||||
| ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { | ||||
|     ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); | ||||
|     sdmc_factory = std::move(factory); | ||||
|     LOG_DEBUG(Service_FS, "Registered SDMC"); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { | ||||
| ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { | ||||
|     ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); | ||||
|     bis_factory = std::move(factory); | ||||
|     LOG_DEBUG(Service_FS, "Registered BIS"); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| void SetPackedUpdate(FileSys::VirtualFile update_raw) { | ||||
| void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { | ||||
|     LOG_TRACE(Service_FS, "Setting packed update for romfs"); | ||||
| 
 | ||||
|     if (romfs_factory == nullptr) | ||||
| @ -297,7 +282,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) { | ||||
|     romfs_factory->SetPackedUpdate(std::move(update_raw)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { | ||||
| ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const { | ||||
|     LOG_TRACE(Service_FS, "Opening RomFS for current process"); | ||||
| 
 | ||||
|     if (romfs_factory == nullptr) { | ||||
| @ -308,8 +293,8 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { | ||||
|     return romfs_factory->OpenCurrentProcess(); | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | ||||
|                                           FileSys::ContentRecordType type) { | ||||
| ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( | ||||
|     u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const { | ||||
|     LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", | ||||
|               title_id, static_cast<u8>(storage_id), static_cast<u8>(type)); | ||||
| 
 | ||||
| @ -321,8 +306,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora | ||||
|     return romfs_factory->Open(title_id, storage_id, type); | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | ||||
|                                             const FileSys::SaveDataDescriptor& descriptor) { | ||||
| ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( | ||||
|     FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const { | ||||
|     LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", | ||||
|               static_cast<u8>(space), save_struct.DebugInfo()); | ||||
| 
 | ||||
|     if (save_data_factory == nullptr) { | ||||
|         return FileSys::ERROR_ENTITY_NOT_FOUND; | ||||
|     } | ||||
| 
 | ||||
|     return save_data_factory->Create(space, save_struct); | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( | ||||
|     FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const { | ||||
|     LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", | ||||
|               static_cast<u8>(space), descriptor.DebugInfo()); | ||||
| 
 | ||||
| @ -333,7 +330,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | ||||
|     return save_data_factory->Open(space, descriptor); | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { | ||||
| ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( | ||||
|     FileSys::SaveDataSpaceId space) const { | ||||
|     LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); | ||||
| 
 | ||||
|     if (save_data_factory == nullptr) { | ||||
| @ -343,7 +341,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) | ||||
|     return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::VirtualDir> OpenSDMC() { | ||||
| ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const { | ||||
|     LOG_TRACE(Service_FS, "Opening SDMC"); | ||||
| 
 | ||||
|     if (sdmc_factory == nullptr) { | ||||
| @ -353,7 +351,92 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() { | ||||
|     return sdmc_factory->Open(); | ||||
| } | ||||
| 
 | ||||
| FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) { | ||||
| ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition( | ||||
|     FileSys::BisPartitionId id) const { | ||||
|     LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id)); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) { | ||||
|         return FileSys::ERROR_ENTITY_NOT_FOUND; | ||||
|     } | ||||
| 
 | ||||
|     auto part = bis_factory->OpenPartition(id); | ||||
|     if (part == nullptr) { | ||||
|         return FileSys::ERROR_INVALID_ARGUMENT; | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<FileSys::VirtualDir>(std::move(part)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage( | ||||
|     FileSys::BisPartitionId id) const { | ||||
|     LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id)); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) { | ||||
|         return FileSys::ERROR_ENTITY_NOT_FOUND; | ||||
|     } | ||||
| 
 | ||||
|     auto part = bis_factory->OpenPartitionStorage(id); | ||||
|     if (part == nullptr) { | ||||
|         return FileSys::ERROR_INVALID_ARGUMENT; | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<FileSys::VirtualFile>(std::move(part)); | ||||
| } | ||||
| 
 | ||||
| u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const { | ||||
|     switch (id) { | ||||
|     case FileSys::StorageId::None: | ||||
|     case FileSys::StorageId::GameCard: | ||||
|         return 0; | ||||
|     case FileSys::StorageId::SdCard: | ||||
|         if (sdmc_factory == nullptr) | ||||
|             return 0; | ||||
|         return sdmc_factory->GetSDMCFreeSpace(); | ||||
|     case FileSys::StorageId::Host: | ||||
|         if (bis_factory == nullptr) | ||||
|             return 0; | ||||
|         return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace(); | ||||
|     case FileSys::StorageId::NandSystem: | ||||
|         if (bis_factory == nullptr) | ||||
|             return 0; | ||||
|         return bis_factory->GetSystemNANDFreeSpace(); | ||||
|     case FileSys::StorageId::NandUser: | ||||
|         if (bis_factory == nullptr) | ||||
|             return 0; | ||||
|         return bis_factory->GetUserNANDFreeSpace(); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const { | ||||
|     switch (id) { | ||||
|     case FileSys::StorageId::None: | ||||
|     case FileSys::StorageId::GameCard: | ||||
|         return 0; | ||||
|     case FileSys::StorageId::SdCard: | ||||
|         if (sdmc_factory == nullptr) | ||||
|             return 0; | ||||
|         return sdmc_factory->GetSDMCTotalSpace(); | ||||
|     case FileSys::StorageId::Host: | ||||
|         if (bis_factory == nullptr) | ||||
|             return 0; | ||||
|         return bis_factory->GetFullNANDTotalSpace(); | ||||
|     case FileSys::StorageId::NandSystem: | ||||
|         if (bis_factory == nullptr) | ||||
|             return 0; | ||||
|         return bis_factory->GetSystemNANDTotalSpace(); | ||||
|     case FileSys::StorageId::NandUser: | ||||
|         if (bis_factory == nullptr) | ||||
|             return 0; | ||||
|         return bis_factory->GetUserNANDTotalSpace(); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type, | ||||
|                                                              u64 title_id, u128 user_id) const { | ||||
|     if (save_data_factory == nullptr) { | ||||
|         return {0, 0}; | ||||
|     } | ||||
| @ -385,13 +468,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, | ||||
|     return value; | ||||
| } | ||||
| 
 | ||||
| void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | ||||
|                        FileSys::SaveDataSize new_value) { | ||||
| void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | ||||
|                                              FileSys::SaveDataSize new_value) const { | ||||
|     if (save_data_factory != nullptr) | ||||
|         save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); | ||||
| } | ||||
| 
 | ||||
| FileSys::RegisteredCache* GetSystemNANDContents() { | ||||
| void FileSystemController::SetGameCard(FileSys::VirtualFile file) { | ||||
|     gamecard = std::make_unique<FileSys::XCI>(file); | ||||
|     const auto dir = gamecard->ConcatenatedPseudoDirectory(); | ||||
|     gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir); | ||||
|     gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir); | ||||
| } | ||||
| 
 | ||||
| FileSys::XCI* FileSystemController::GetGameCard() const { | ||||
|     return gamecard.get(); | ||||
| } | ||||
| 
 | ||||
| FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const { | ||||
|     return gamecard_registered.get(); | ||||
| } | ||||
| 
 | ||||
| FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const { | ||||
|     return gamecard_placeholder.get(); | ||||
| } | ||||
| 
 | ||||
| FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const { | ||||
|     LOG_TRACE(Service_FS, "Opening System NAND Contents"); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
| @ -400,7 +502,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() { | ||||
|     return bis_factory->GetSystemNANDContents(); | ||||
| } | ||||
| 
 | ||||
| FileSys::RegisteredCache* GetUserNANDContents() { | ||||
| FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const { | ||||
|     LOG_TRACE(Service_FS, "Opening User NAND Contents"); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
| @ -409,7 +511,7 @@ FileSys::RegisteredCache* GetUserNANDContents() { | ||||
|     return bis_factory->GetUserNANDContents(); | ||||
| } | ||||
| 
 | ||||
| FileSys::RegisteredCache* GetSDMCContents() { | ||||
| FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const { | ||||
|     LOG_TRACE(Service_FS, "Opening SDMC Contents"); | ||||
| 
 | ||||
|     if (sdmc_factory == nullptr) | ||||
| @ -418,7 +520,143 @@ FileSys::RegisteredCache* GetSDMCContents() { | ||||
|     return sdmc_factory->GetSDMCContents(); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { | ||||
| FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const { | ||||
|     LOG_TRACE(Service_FS, "Opening System NAND Placeholder"); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return bis_factory->GetSystemNANDPlaceholder(); | ||||
| } | ||||
| 
 | ||||
| FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const { | ||||
|     LOG_TRACE(Service_FS, "Opening User NAND Placeholder"); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return bis_factory->GetUserNANDPlaceholder(); | ||||
| } | ||||
| 
 | ||||
| FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const { | ||||
|     LOG_TRACE(Service_FS, "Opening SDMC Placeholder"); | ||||
| 
 | ||||
|     if (sdmc_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return sdmc_factory->GetSDMCPlaceholder(); | ||||
| } | ||||
| 
 | ||||
| FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage( | ||||
|     FileSys::StorageId id) const { | ||||
|     switch (id) { | ||||
|     case FileSys::StorageId::None: | ||||
|     case FileSys::StorageId::Host: | ||||
|         UNIMPLEMENTED(); | ||||
|         return nullptr; | ||||
|     case FileSys::StorageId::GameCard: | ||||
|         return GetGameCardContents(); | ||||
|     case FileSys::StorageId::NandSystem: | ||||
|         return GetSystemNANDContents(); | ||||
|     case FileSys::StorageId::NandUser: | ||||
|         return GetUserNANDContents(); | ||||
|     case FileSys::StorageId::SdCard: | ||||
|         return GetSDMCContents(); | ||||
|     } | ||||
| 
 | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage( | ||||
|     FileSys::StorageId id) const { | ||||
|     switch (id) { | ||||
|     case FileSys::StorageId::None: | ||||
|     case FileSys::StorageId::Host: | ||||
|         UNIMPLEMENTED(); | ||||
|         return nullptr; | ||||
|     case FileSys::StorageId::GameCard: | ||||
|         return GetGameCardPlaceholder(); | ||||
|     case FileSys::StorageId::NandSystem: | ||||
|         return GetSystemNANDPlaceholder(); | ||||
|     case FileSys::StorageId::NandUser: | ||||
|         return GetUserNANDPlaceholder(); | ||||
|     case FileSys::StorageId::SdCard: | ||||
|         return GetSDMCPlaceholder(); | ||||
|     } | ||||
| 
 | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const { | ||||
|     LOG_TRACE(Service_FS, "Opening system NAND content directory"); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return bis_factory->GetSystemNANDContentDirectory(); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const { | ||||
|     LOG_TRACE(Service_FS, "Opening user NAND content directory"); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return bis_factory->GetUserNANDContentDirectory(); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const { | ||||
|     LOG_TRACE(Service_FS, "Opening SDMC content directory"); | ||||
| 
 | ||||
|     if (sdmc_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return sdmc_factory->GetSDMCContentDirectory(); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const { | ||||
|     LOG_TRACE(Service_FS, "Opening NAND image directory"); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return bis_factory->GetImageDirectory(); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const { | ||||
|     LOG_TRACE(Service_FS, "Opening SDMC image directory"); | ||||
| 
 | ||||
|     if (sdmc_factory == nullptr) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     return sdmc_factory->GetImageDirectory(); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const { | ||||
|     switch (id) { | ||||
|     case ContentStorageId::System: | ||||
|         return GetSystemNANDContentDirectory(); | ||||
|     case ContentStorageId::User: | ||||
|         return GetUserNANDContentDirectory(); | ||||
|     case ContentStorageId::SdCard: | ||||
|         return GetSDMCContentDirectory(); | ||||
|     } | ||||
| 
 | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const { | ||||
|     switch (id) { | ||||
|     case ImageDirectoryId::NAND: | ||||
|         return GetNANDImageDirectory(); | ||||
|     case ImageDirectoryId::SdCard: | ||||
|         return GetSDMCImageDirectory(); | ||||
|     } | ||||
| 
 | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const { | ||||
|     LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
| @ -427,7 +665,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { | ||||
|     return bis_factory->GetModificationLoadRoot(title_id); | ||||
| } | ||||
| 
 | ||||
| FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { | ||||
| FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const { | ||||
|     LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); | ||||
| 
 | ||||
|     if (bis_factory == nullptr) | ||||
| @ -436,7 +674,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { | ||||
|     return bis_factory->GetModificationDumpRoot(title_id); | ||||
| } | ||||
| 
 | ||||
| void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | ||||
| void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | ||||
|     if (overwrite) { | ||||
|         bis_factory = nullptr; | ||||
|         save_data_factory = nullptr; | ||||
| @ -473,11 +711,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | ||||
| } | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system) { | ||||
|     romfs_factory = nullptr; | ||||
|     CreateFactories(*system.GetFilesystem(), false); | ||||
|     std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); | ||||
|     std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); | ||||
|     std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager()); | ||||
|     std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter()) | ||||
|         ->InstallAsService(system.ServiceManager()); | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::FileSystem
 | ||||
|  | ||||
| @ -14,10 +14,13 @@ namespace FileSys { | ||||
| class BISFactory; | ||||
| class RegisteredCache; | ||||
| class RegisteredCacheUnion; | ||||
| class PlaceholderCache; | ||||
| class RomFSFactory; | ||||
| class SaveDataFactory; | ||||
| class SDMCFactory; | ||||
| class XCI; | ||||
| 
 | ||||
| enum class BisPartitionId : u32; | ||||
| enum class ContentRecordType : u8; | ||||
| enum class Mode : u32; | ||||
| enum class SaveDataSpaceId : u8; | ||||
| @ -36,34 +39,91 @@ class ServiceManager; | ||||
| 
 | ||||
| namespace FileSystem { | ||||
| 
 | ||||
| ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); | ||||
| ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); | ||||
| ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); | ||||
| ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); | ||||
| enum class ContentStorageId : u32 { | ||||
|     System, | ||||
|     User, | ||||
|     SdCard, | ||||
| }; | ||||
| 
 | ||||
| void SetPackedUpdate(FileSys::VirtualFile update_raw); | ||||
| ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); | ||||
| ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | ||||
|                                           FileSys::ContentRecordType type); | ||||
| ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | ||||
|                                             const FileSys::SaveDataDescriptor& descriptor); | ||||
| ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); | ||||
| ResultVal<FileSys::VirtualDir> OpenSDMC(); | ||||
| enum class ImageDirectoryId : u32 { | ||||
|     NAND, | ||||
|     SdCard, | ||||
| }; | ||||
| 
 | ||||
| FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); | ||||
| void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | ||||
|                        FileSys::SaveDataSize new_value); | ||||
| class FileSystemController { | ||||
| public: | ||||
|     FileSystemController(); | ||||
|     ~FileSystemController(); | ||||
| 
 | ||||
| FileSys::RegisteredCache* GetSystemNANDContents(); | ||||
| FileSys::RegisteredCache* GetUserNANDContents(); | ||||
| FileSys::RegisteredCache* GetSDMCContents(); | ||||
|     ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); | ||||
|     ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); | ||||
|     ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); | ||||
|     ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); | ||||
| 
 | ||||
| FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); | ||||
| FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); | ||||
|     void SetPackedUpdate(FileSys::VirtualFile update_raw); | ||||
|     ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const; | ||||
|     ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | ||||
|                                               FileSys::ContentRecordType type) const; | ||||
|     ResultVal<FileSys::VirtualDir> CreateSaveData( | ||||
|         FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | ||||
|     ResultVal<FileSys::VirtualDir> OpenSaveData( | ||||
|         FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | ||||
|     ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; | ||||
|     ResultVal<FileSys::VirtualDir> OpenSDMC() const; | ||||
|     ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; | ||||
|     ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const; | ||||
| 
 | ||||
| // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
 | ||||
| // above is called.
 | ||||
| void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); | ||||
|     u64 GetFreeSpaceSize(FileSys::StorageId id) const; | ||||
|     u64 GetTotalSpaceSize(FileSys::StorageId id) const; | ||||
| 
 | ||||
|     FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, | ||||
|                                            u128 user_id) const; | ||||
|     void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | ||||
|                            FileSys::SaveDataSize new_value) const; | ||||
| 
 | ||||
|     void SetGameCard(FileSys::VirtualFile file); | ||||
|     FileSys::XCI* GetGameCard() const; | ||||
| 
 | ||||
|     FileSys::RegisteredCache* GetSystemNANDContents() const; | ||||
|     FileSys::RegisteredCache* GetUserNANDContents() const; | ||||
|     FileSys::RegisteredCache* GetSDMCContents() const; | ||||
|     FileSys::RegisteredCache* GetGameCardContents() const; | ||||
| 
 | ||||
|     FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const; | ||||
|     FileSys::PlaceholderCache* GetUserNANDPlaceholder() const; | ||||
|     FileSys::PlaceholderCache* GetSDMCPlaceholder() const; | ||||
|     FileSys::PlaceholderCache* GetGameCardPlaceholder() const; | ||||
| 
 | ||||
|     FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const; | ||||
|     FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const; | ||||
| 
 | ||||
|     FileSys::VirtualDir GetSystemNANDContentDirectory() const; | ||||
|     FileSys::VirtualDir GetUserNANDContentDirectory() const; | ||||
|     FileSys::VirtualDir GetSDMCContentDirectory() const; | ||||
| 
 | ||||
|     FileSys::VirtualDir GetNANDImageDirectory() const; | ||||
|     FileSys::VirtualDir GetSDMCImageDirectory() const; | ||||
| 
 | ||||
|     FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const; | ||||
|     FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const; | ||||
| 
 | ||||
|     FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; | ||||
|     FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; | ||||
| 
 | ||||
|     // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
 | ||||
|     // above is called.
 | ||||
|     void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<FileSys::RomFSFactory> romfs_factory; | ||||
|     std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; | ||||
|     std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; | ||||
|     std::unique_ptr<FileSys::BISFactory> bis_factory; | ||||
| 
 | ||||
|     std::unique_ptr<FileSys::XCI> gamecard; | ||||
|     std::unique_ptr<FileSys::RegisteredCache> gamecard_registered; | ||||
|     std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder; | ||||
| }; | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system); | ||||
| 
 | ||||
| @ -159,12 +219,6 @@ public: | ||||
|      */ | ||||
|     ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the free space | ||||
|      * @return The number of free bytes in the archive | ||||
|      */ | ||||
|     u64 GetFreeSpaceSize() const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the type of the specified path | ||||
|      * @return The type of the specified path or error code | ||||
|  | ||||
| @ -19,6 +19,7 @@ | ||||
| #include "core/file_sys/mode.h" | ||||
| #include "core/file_sys/nca_metadata.h" | ||||
| #include "core/file_sys/patch_manager.h" | ||||
| #include "core/file_sys/romfs_factory.h" | ||||
| #include "core/file_sys/savedata_factory.h" | ||||
| #include "core/file_sys/system_archive/system_archive.h" | ||||
| #include "core/file_sys/vfs.h" | ||||
| @ -30,6 +31,18 @@ | ||||
| 
 | ||||
| namespace Service::FileSystem { | ||||
| 
 | ||||
| struct SizeGetter { | ||||
|     std::function<u64()> get_free_size; | ||||
|     std::function<u64()> get_total_size; | ||||
| 
 | ||||
|     static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) { | ||||
|         return { | ||||
|             [&fsc, id] { return fsc.GetFreeSpaceSize(id); }, | ||||
|             [&fsc, id] { return fsc.GetTotalSpaceSize(id); }, | ||||
|         }; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| enum class FileSystemType : u8 { | ||||
|     Invalid0 = 0, | ||||
|     Invalid1 = 1, | ||||
| @ -289,8 +302,8 @@ private: | ||||
| 
 | ||||
| class IFileSystem final : public ServiceFramework<IFileSystem> { | ||||
| public: | ||||
|     explicit IFileSystem(FileSys::VirtualDir backend) | ||||
|         : ServiceFramework("IFileSystem"), backend(std::move(backend)) { | ||||
|     explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size) | ||||
|         : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IFileSystem::CreateFile, "CreateFile"}, | ||||
|             {1, &IFileSystem::DeleteFile, "DeleteFile"}, | ||||
| @ -467,14 +480,31 @@ public: | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|     } | ||||
| 
 | ||||
|     void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_FS, "called"); | ||||
| 
 | ||||
|         IPC::ResponseBuilder rb{ctx, 4}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.Push(size.get_free_size()); | ||||
|     } | ||||
| 
 | ||||
|     void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_FS, "called"); | ||||
| 
 | ||||
|         IPC::ResponseBuilder rb{ctx, 4}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.Push(size.get_total_size()); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     VfsDirectoryServiceWrapper backend; | ||||
|     SizeGetter size; | ||||
| }; | ||||
| 
 | ||||
| class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { | ||||
| public: | ||||
|     explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) | ||||
|         : ServiceFramework("ISaveDataInfoReader") { | ||||
|     explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc) | ||||
|         : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, | ||||
|         }; | ||||
| @ -520,8 +550,13 @@ private: | ||||
|     } | ||||
| 
 | ||||
|     void FindAllSaves(FileSys::SaveDataSpaceId space) { | ||||
|         const auto save_root = OpenSaveDataSpace(space); | ||||
|         ASSERT(save_root.Succeeded()); | ||||
|         const auto save_root = fsc.OpenSaveDataSpace(space); | ||||
| 
 | ||||
|         if (save_root.Failed() || *save_root == nullptr) { | ||||
|             LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", | ||||
|                       static_cast<u8>(space)); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         for (const auto& type : (*save_root)->GetSubdirectories()) { | ||||
|             if (type->GetName() == "save") { | ||||
| @ -610,11 +645,13 @@ private: | ||||
|     }; | ||||
|     static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); | ||||
| 
 | ||||
|     FileSystemController& fsc; | ||||
|     std::vector<SaveDataInfo> info; | ||||
|     u64 next_entry_index = 0; | ||||
| }; | ||||
| 
 | ||||
| FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) { | ||||
| FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) | ||||
|     : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) { | ||||
|     // clang-format off
 | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0, nullptr, "OpenFileSystem"}, | ||||
| @ -754,7 +791,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) { | ||||
| void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_FS, "called"); | ||||
| 
 | ||||
|     IFileSystem filesystem(OpenSDMC().Unwrap()); | ||||
|     IFileSystem filesystem(fsc.OpenSDMC().Unwrap(), | ||||
|                            SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| @ -768,8 +806,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | ||||
|     auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); | ||||
|     u128 uid = rp.PopRaw<u128>(); | ||||
| 
 | ||||
|     LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}", | ||||
|                 save_struct.DebugInfo(), uid[1], uid[0]); | ||||
|     LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), | ||||
|               uid[1], uid[0]); | ||||
| 
 | ||||
|     fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct); | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| @ -786,14 +826,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto parameters = rp.PopRaw<Parameters>(); | ||||
| 
 | ||||
|     auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor); | ||||
|     auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor); | ||||
|     if (dir.Failed()) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | ||||
|         rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IFileSystem filesystem(std::move(dir.Unwrap())); | ||||
|     FileSys::StorageId id; | ||||
|     if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) { | ||||
|         id = FileSys::StorageId::NandUser; | ||||
|     } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem || | ||||
|                parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) { | ||||
|         id = FileSys::StorageId::SdCard; | ||||
|     } else { | ||||
|         id = FileSys::StorageId::NandSystem; | ||||
|     } | ||||
| 
 | ||||
|     IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| @ -812,7 +862,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); | ||||
|     rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); | ||||
| } | ||||
| 
 | ||||
| void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||||
| @ -836,7 +886,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||||
| void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_FS, "called"); | ||||
| 
 | ||||
|     auto romfs = OpenRomFSCurrentProcess(); | ||||
|     auto romfs = fsc.OpenRomFSCurrentProcess(); | ||||
|     if (romfs.Failed()) { | ||||
|         // TODO (bunnei): Find the right error code to use here
 | ||||
|         LOG_CRITICAL(Service_FS, "no file system interface available!"); | ||||
| @ -861,7 +911,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", | ||||
|               static_cast<u8>(storage_id), unknown, title_id); | ||||
| 
 | ||||
|     auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); | ||||
|     auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); | ||||
| 
 | ||||
|     if (data.Failed()) { | ||||
|         const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); | ||||
|  | ||||
| @ -32,7 +32,7 @@ enum class LogMode : u32 { | ||||
| 
 | ||||
| class FSP_SRV final : public ServiceFramework<FSP_SRV> { | ||||
| public: | ||||
|     explicit FSP_SRV(const Core::Reporter& reporter); | ||||
|     explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter); | ||||
|     ~FSP_SRV() override; | ||||
| 
 | ||||
| private: | ||||
| @ -51,6 +51,8 @@ private: | ||||
|     void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); | ||||
|     void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|     FileSystemController& fsc; | ||||
| 
 | ||||
|     FileSys::VirtualFile romfs; | ||||
|     u64 current_process_id = 0; | ||||
|     u32 access_log_program_index = 0; | ||||
|  | ||||
| @ -617,7 +617,7 @@ public: | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager) { | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc) { | ||||
|     std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); | ||||
|     std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); | ||||
|     std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); | ||||
| @ -628,7 +628,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { | ||||
|     std::make_shared<NS_SU>()->InstallAsService(service_manager); | ||||
|     std::make_shared<NS_VM>()->InstallAsService(service_manager); | ||||
| 
 | ||||
|     std::make_shared<PL_U>()->InstallAsService(service_manager); | ||||
|     std::make_shared<PL_U>(fsc)->InstallAsService(service_manager); | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::NS
 | ||||
|  | ||||
| @ -6,7 +6,13 @@ | ||||
| 
 | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Service::NS { | ||||
| namespace Service { | ||||
| 
 | ||||
| namespace FileSystem { | ||||
| class FileSystemController; | ||||
| } // namespace FileSystem
 | ||||
| 
 | ||||
| namespace NS { | ||||
| 
 | ||||
| class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { | ||||
| public: | ||||
| @ -91,6 +97,8 @@ private: | ||||
| }; | ||||
| 
 | ||||
| /// Registers all NS services with the specified service manager.
 | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager); | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc); | ||||
| 
 | ||||
| } // namespace Service::NS
 | ||||
| } // namespace NS
 | ||||
| 
 | ||||
| } // namespace Service
 | ||||
|  | ||||
| @ -150,7 +150,8 @@ struct PL_U::Impl { | ||||
|     std::vector<FontRegion> shared_font_regions; | ||||
| }; | ||||
| 
 | ||||
| PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { | ||||
| PL_U::PL_U(FileSystem::FileSystemController& fsc) | ||||
|     : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0, &PL_U::RequestLoad, "RequestLoad"}, | ||||
|         {1, &PL_U::GetLoadState, "GetLoadState"}, | ||||
| @ -161,7 +162,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { | ||||
|     }; | ||||
|     RegisterHandlers(functions); | ||||
|     // Attempt to load shared font data from disk
 | ||||
|     const auto* nand = FileSystem::GetSystemNANDContents(); | ||||
|     const auto* nand = fsc.GetSystemNANDContents(); | ||||
|     std::size_t offset = 0; | ||||
|     // Rebuild shared fonts from data ncas
 | ||||
|     if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), | ||||
|  | ||||
| @ -7,11 +7,17 @@ | ||||
| #include <memory> | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Service::NS { | ||||
| namespace Service { | ||||
| 
 | ||||
| namespace FileSystem { | ||||
| class FileSystemController; | ||||
| } // namespace FileSystem
 | ||||
| 
 | ||||
| namespace NS { | ||||
| 
 | ||||
| class PL_U final : public ServiceFramework<PL_U> { | ||||
| public: | ||||
|     PL_U(); | ||||
|     PL_U(FileSystem::FileSystemController& fsc); | ||||
|     ~PL_U() override; | ||||
| 
 | ||||
| private: | ||||
| @ -26,4 +32,6 @@ private: | ||||
|     std::unique_ptr<Impl> impl; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Service::NS
 | ||||
| } // namespace NS
 | ||||
| 
 | ||||
| } // namespace Service
 | ||||
|  | ||||
| @ -199,6 +199,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | ||||
|     // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
 | ||||
|     // here and pass it into the respective InstallInterfaces functions.
 | ||||
|     auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); | ||||
|     system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); | ||||
| 
 | ||||
|     SM::ServiceManager::InstallInterfaces(sm); | ||||
| 
 | ||||
| @ -235,7 +236,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | ||||
|     NIFM::InstallInterfaces(*sm); | ||||
|     NIM::InstallInterfaces(*sm); | ||||
|     NPNS::InstallInterfaces(*sm); | ||||
|     NS::InstallInterfaces(*sm); | ||||
|     NS::InstallInterfaces(*sm, system.GetFileSystemController()); | ||||
|     Nvidia::InstallInterfaces(*sm, *nv_flinger, system); | ||||
|     PCIe::InstallInterfaces(*sm); | ||||
|     PCTL::InstallInterfaces(*sm); | ||||
|  | ||||
| @ -18,10 +18,6 @@ namespace Core { | ||||
| class System; | ||||
| } | ||||
| 
 | ||||
| namespace FileSys { | ||||
| class VfsFilesystem; | ||||
| } | ||||
| 
 | ||||
| namespace Kernel { | ||||
| class ClientPort; | ||||
| class ServerPort; | ||||
| @ -31,6 +27,10 @@ class HLERequestContext; | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| namespace FileSystem { | ||||
| class FileSystemController; | ||||
| } // namespace FileSystem
 | ||||
| 
 | ||||
| namespace SM { | ||||
| class ServiceManager; | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/content_archive.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| #include "core/file_sys/patch_manager.h" | ||||
| @ -176,7 +177,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | ||||
|     // Register the RomFS if a ".romfs" file was found
 | ||||
|     if (romfs_iter != files.end() && *romfs_iter != nullptr) { | ||||
|         romfs = *romfs_iter; | ||||
|         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | ||||
|         Core::System::GetInstance().GetFileSystemController().RegisterRomFS( | ||||
|             std::make_unique<FileSys::RomFSFactory>(*this)); | ||||
|     } | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| 
 | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/content_archive.h" | ||||
| #include "core/file_sys/romfs_factory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| @ -57,7 +58,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { | ||||
|     } | ||||
| 
 | ||||
|     if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { | ||||
|         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | ||||
|         Core::System::GetInstance().GetFileSystemController().RegisterRomFS( | ||||
|             std::make_unique<FileSys::RomFSFactory>(*this)); | ||||
|     } | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|  | ||||
| @ -10,6 +10,7 @@ | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/swap.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| #include "core/file_sys/romfs_factory.h" | ||||
| #include "core/file_sys/vfs_offset.h" | ||||
| @ -214,7 +215,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { | ||||
|     } | ||||
| 
 | ||||
|     if (romfs != nullptr) { | ||||
|         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | ||||
|         Core::System::GetInstance().GetFileSystemController().RegisterRomFS( | ||||
|             std::make_unique<FileSys::RomFSFactory>(*this)); | ||||
|     } | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/card_image.h" | ||||
| #include "core/file_sys/content_archive.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| @ -105,7 +106,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { | ||||
| 
 | ||||
|     FileSys::VirtualFile update_raw; | ||||
|     if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { | ||||
|         Service::FileSystem::SetPackedUpdate(std::move(update_raw)); | ||||
|         Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( | ||||
|             std::move(update_raw)); | ||||
|     } | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/card_image.h" | ||||
| #include "core/file_sys/content_archive.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| @ -72,7 +73,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { | ||||
| 
 | ||||
|     FileSys::VirtualFile update_raw; | ||||
|     if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { | ||||
|         Service::FileSystem::SetPackedUpdate(std::move(update_raw)); | ||||
|         Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( | ||||
|             std::move(update_raw)); | ||||
|     } | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/file_util.h" | ||||
| #include "core/core.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/service/hid/hid.h" | ||||
| @ -97,8 +98,8 @@ void LogSettings() { | ||||
|     LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | ||||
|     LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); | ||||
|     LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); | ||||
|     LogSetting("DataStorage_NandDir", Settings::values.nand_dir); | ||||
|     LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir); | ||||
|     LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); | ||||
|     LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); | ||||
|     LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); | ||||
|     LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); | ||||
|     LogSetting("Debugging_ProgramArgs", Settings::values.program_args); | ||||
|  | ||||
| @ -346,6 +346,31 @@ struct TouchscreenInput { | ||||
|     u32 rotation_angle; | ||||
| }; | ||||
| 
 | ||||
| enum class NANDTotalSize : u64 { | ||||
|     S29_1GB = 0x747C00000ULL, | ||||
| }; | ||||
| 
 | ||||
| enum class NANDUserSize : u64 { | ||||
|     S26GB = 0x680000000ULL, | ||||
| }; | ||||
| 
 | ||||
| enum class NANDSystemSize : u64 { | ||||
|     S2_5GB = 0xA0000000, | ||||
| }; | ||||
| 
 | ||||
| enum class SDMCSize : u64 { | ||||
|     S1GB = 0x40000000, | ||||
|     S2GB = 0x80000000, | ||||
|     S4GB = 0x100000000ULL, | ||||
|     S8GB = 0x200000000ULL, | ||||
|     S16GB = 0x400000000ULL, | ||||
|     S32GB = 0x800000000ULL, | ||||
|     S64GB = 0x1000000000ULL, | ||||
|     S128GB = 0x2000000000ULL, | ||||
|     S256GB = 0x4000000000ULL, | ||||
|     S1TB = 0x10000000000ULL, | ||||
| }; | ||||
| 
 | ||||
| struct Values { | ||||
|     // System
 | ||||
|     bool use_docked_mode; | ||||
| @ -382,8 +407,13 @@ struct Values { | ||||
| 
 | ||||
|     // Data Storage
 | ||||
|     bool use_virtual_sd; | ||||
|     std::string nand_dir; | ||||
|     std::string sdmc_dir; | ||||
|     bool gamecard_inserted; | ||||
|     bool gamecard_current_game; | ||||
|     std::string gamecard_path; | ||||
|     NANDTotalSize nand_total_size; | ||||
|     NANDSystemSize nand_system_size; | ||||
|     NANDUserSize nand_user_size; | ||||
|     SDMCSize sdmc_size; | ||||
| 
 | ||||
|     // Renderer
 | ||||
|     float resolution_factor; | ||||
|  | ||||
| @ -33,6 +33,9 @@ add_executable(yuzu | ||||
|     configuration/configure_debug.ui | ||||
|     configuration/configure_dialog.cpp | ||||
|     configuration/configure_dialog.h | ||||
|     configuration/configure_filesystem.cpp | ||||
|     configuration/configure_filesystem.h | ||||
|     configuration/configure_filesystem.ui | ||||
|     configuration/configure_gamelist.cpp | ||||
|     configuration/configure_gamelist.h | ||||
|     configuration/configure_gamelist.ui | ||||
|  | ||||
| @ -459,6 +459,49 @@ void Config::ReadDataStorageValues() { | ||||
|                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) | ||||
|             .toString() | ||||
|             .toStdString()); | ||||
|     FileUtil::GetUserPath( | ||||
|         FileUtil::UserPath::LoadDir, | ||||
|         qt_config | ||||
|             ->value(QStringLiteral("load_directory"), | ||||
|                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))) | ||||
|             .toString() | ||||
|             .toStdString()); | ||||
|     FileUtil::GetUserPath( | ||||
|         FileUtil::UserPath::DumpDir, | ||||
|         qt_config | ||||
|             ->value(QStringLiteral("dump_directory"), | ||||
|                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))) | ||||
|             .toString() | ||||
|             .toStdString()); | ||||
|     FileUtil::GetUserPath( | ||||
|         FileUtil::UserPath::CacheDir, | ||||
|         qt_config | ||||
|             ->value(QStringLiteral("cache_directory"), | ||||
|                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))) | ||||
|             .toString() | ||||
|             .toStdString()); | ||||
|     Settings::values.gamecard_inserted = | ||||
|         ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); | ||||
|     Settings::values.gamecard_current_game = | ||||
|         ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); | ||||
|     Settings::values.gamecard_path = | ||||
|         ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); | ||||
|     Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( | ||||
|         ReadSetting(QStringLiteral("nand_total_size"), | ||||
|                     QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))) | ||||
|             .toULongLong()); | ||||
|     Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( | ||||
|         ReadSetting(QStringLiteral("nand_user_size"), | ||||
|                     QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))) | ||||
|             .toULongLong()); | ||||
|     Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||||
|         ReadSetting(QStringLiteral("nand_system_size"), | ||||
|                     QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))) | ||||
|             .toULongLong()); | ||||
|     Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( | ||||
|         ReadSetting(QStringLiteral("sdmc_size"), | ||||
|                     QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))) | ||||
|             .toULongLong()); | ||||
| 
 | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| @ -875,7 +918,32 @@ void Config::SaveDataStorageValues() { | ||||
|     WriteSetting(QStringLiteral("sdmc_directory"), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | ||||
| 
 | ||||
|     WriteSetting(QStringLiteral("load_directory"), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); | ||||
|     WriteSetting(QStringLiteral("dump_directory"), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); | ||||
|     WriteSetting(QStringLiteral("cache_directory"), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)), | ||||
|                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); | ||||
|     WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); | ||||
|     WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, | ||||
|                  false); | ||||
|     WriteSetting(QStringLiteral("gamecard_path"), | ||||
|                  QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); | ||||
|     WriteSetting(QStringLiteral("nand_total_size"), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))); | ||||
|     WriteSetting(QStringLiteral("nand_user_size"), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))); | ||||
|     WriteSetting(QStringLiteral("nand_system_size"), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))); | ||||
|     WriteSetting(QStringLiteral("sdmc_size"), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)), | ||||
|                  QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -63,6 +63,11 @@ | ||||
|          <string>Profiles</string> | ||||
|         </attribute> | ||||
|        </widget> | ||||
|        <widget class="ConfigureFilesystem" name="filesystemTab"> | ||||
|         <attribute name="title"> | ||||
|          <string>Filesystem</string> | ||||
|         </attribute> | ||||
|        </widget> | ||||
|        <widget class="ConfigureInputSimple" name="inputTab"> | ||||
|         <attribute name="title"> | ||||
|          <string>Input</string> | ||||
| @ -125,6 +130,12 @@ | ||||
|    <header>configuration/configure_profile_manager.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|   <customwidget> | ||||
|    <class>ConfigureFilesystem</class> | ||||
|    <extends>QWidget</extends> | ||||
|    <header>configuration/configure_filesystem.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|   <customwidget> | ||||
|    <class>ConfigureAudio</class> | ||||
|    <extends>QWidget</extends> | ||||
|  | ||||
| @ -34,8 +34,6 @@ void ConfigureDebug::SetConfiguration() { | ||||
|     ui->toggle_console->setChecked(UISettings::values.show_console); | ||||
|     ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); | ||||
|     ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | ||||
|     ui->dump_exefs->setChecked(Settings::values.dump_exefs); | ||||
|     ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); | ||||
|     ui->reporting_services->setChecked(Settings::values.reporting_services); | ||||
|     ui->quest_flag->setChecked(Settings::values.quest_flag); | ||||
| } | ||||
| @ -46,8 +44,6 @@ void ConfigureDebug::ApplyConfiguration() { | ||||
|     UISettings::values.show_console = ui->toggle_console->isChecked(); | ||||
|     Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); | ||||
|     Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | ||||
|     Settings::values.dump_exefs = ui->dump_exefs->isChecked(); | ||||
|     Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); | ||||
|     Settings::values.reporting_services = ui->reporting_services->isChecked(); | ||||
|     Settings::values.quest_flag = ui->quest_flag->isChecked(); | ||||
|     Debugger::ToggleConsole(); | ||||
|  | ||||
| @ -103,58 +103,6 @@ | ||||
|         </item> | ||||
|        </layout> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QGroupBox" name="groupBox_3"> | ||||
|      <property name="title"> | ||||
|       <string>Homebrew</string> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|       <item> | ||||
|        <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||||
|         <item> | ||||
|          <widget class="QLabel" name="label_3"> | ||||
|           <property name="text"> | ||||
|            <string>Arguments String</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QLineEdit" name="homebrew_args_edit"/> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QGroupBox" name="groupBox_4"> | ||||
|      <property name="title"> | ||||
|       <string>Dump</string> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||
|       <item> | ||||
|        <widget class="QCheckBox" name="dump_decompressed_nso"> | ||||
|         <property name="whatsThis"> | ||||
|          <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Dump Decompressed NSOs</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QCheckBox" name="dump_exefs"> | ||||
|         <property name="whatsThis"> | ||||
|          <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Dump ExeFS</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QCheckBox" name="reporting_services"> | ||||
|         <property name="text"> | ||||
| @ -163,7 +111,7 @@ | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="label_3"> | ||||
|        <widget class="QLabel" name="label"> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <italic>true</italic> | ||||
| @ -196,11 +144,37 @@ | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QGroupBox" name="groupBox_3"> | ||||
|      <property name="title"> | ||||
|       <string>Homebrew</string> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|       <item> | ||||
|        <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||||
|         <item> | ||||
|          <widget class="QLabel" name="label_3"> | ||||
|           <property name="text"> | ||||
|            <string>Arguments String</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QLineEdit" name="homebrew_args_edit"/> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <spacer name="verticalSpacer"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Vertical</enum> | ||||
|      </property> | ||||
|      <property name="sizeType"> | ||||
|       <enum>QSizePolicy::Expanding</enum> | ||||
|      </property> | ||||
|      <property name="sizeHint" stdset="0"> | ||||
|       <size> | ||||
|        <width>20</width> | ||||
|  | ||||
| @ -37,6 +37,7 @@ void ConfigureDialog::ApplyConfiguration() { | ||||
|     ui->gameListTab->ApplyConfiguration(); | ||||
|     ui->systemTab->ApplyConfiguration(); | ||||
|     ui->profileManagerTab->ApplyConfiguration(); | ||||
|     ui->filesystemTab->applyConfiguration(); | ||||
|     ui->inputTab->ApplyConfiguration(); | ||||
|     ui->hotkeysTab->ApplyConfiguration(registry); | ||||
|     ui->graphicsTab->ApplyConfiguration(); | ||||
| @ -73,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>); | ||||
| void ConfigureDialog::PopulateSelectionList() { | ||||
|     const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ | ||||
|         {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, | ||||
|          {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->audioTab}}, | ||||
|          {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}}, | ||||
|          {tr("Graphics"), {ui->graphicsTab}}, | ||||
|          {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, | ||||
|     }; | ||||
| @ -106,6 +107,7 @@ void ConfigureDialog::UpdateVisibleTabs() { | ||||
|         {ui->debugTab, tr("Debug")}, | ||||
|         {ui->webTab, tr("Web")}, | ||||
|         {ui->gameListTab, tr("Game List")}, | ||||
|         {ui->filesystemTab, tr("Filesystem")}, | ||||
|     }; | ||||
| 
 | ||||
|     [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); | ||||
|  | ||||
							
								
								
									
										177
									
								
								src/yuzu/configuration/configure_filesystem.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/yuzu/configuration/configure_filesystem.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,177 @@ | ||||
| // Copyright 2019 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QFileDialog> | ||||
| #include <QMessageBox> | ||||
| #include "common/common_paths.h" | ||||
| #include "common/file_util.h" | ||||
| #include "core/settings.h" | ||||
| #include "ui_configure_filesystem.h" | ||||
| #include "yuzu/configuration/configure_filesystem.h" | ||||
| #include "yuzu/uisettings.h" | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| template <typename T> | ||||
| void SetComboBoxFromData(QComboBox* combo_box, T data) { | ||||
|     const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data))); | ||||
|     if (index >= combo_box->count() || index < 0) | ||||
|         return; | ||||
| 
 | ||||
|     combo_box->setCurrentIndex(index); | ||||
| } | ||||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| ConfigureFilesystem::ConfigureFilesystem(QWidget* parent) | ||||
|     : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) { | ||||
|     ui->setupUi(this); | ||||
|     this->setConfiguration(); | ||||
| 
 | ||||
|     connect(ui->nand_directory_button, &QToolButton::pressed, this, | ||||
|             [this] { SetDirectory(DirectoryTarget::NAND, ui->nand_directory_edit); }); | ||||
|     connect(ui->sdmc_directory_button, &QToolButton::pressed, this, | ||||
|             [this] { SetDirectory(DirectoryTarget::SD, ui->sdmc_directory_edit); }); | ||||
|     connect(ui->gamecard_path_button, &QToolButton::pressed, this, | ||||
|             [this] { SetDirectory(DirectoryTarget::Gamecard, ui->gamecard_path_edit); }); | ||||
|     connect(ui->dump_path_button, &QToolButton::pressed, this, | ||||
|             [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); }); | ||||
|     connect(ui->load_path_button, &QToolButton::pressed, this, | ||||
|             [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); }); | ||||
|     connect(ui->cache_directory_button, &QToolButton::pressed, this, | ||||
|             [this] { SetDirectory(DirectoryTarget::Cache, ui->cache_directory_edit); }); | ||||
| 
 | ||||
|     connect(ui->reset_game_list_cache, &QPushButton::pressed, this, | ||||
|             &ConfigureFilesystem::ResetMetadata); | ||||
| 
 | ||||
|     connect(ui->gamecard_inserted, &QCheckBox::stateChanged, this, | ||||
|             &ConfigureFilesystem::UpdateEnabledControls); | ||||
|     connect(ui->gamecard_current_game, &QCheckBox::stateChanged, this, | ||||
|             &ConfigureFilesystem::UpdateEnabledControls); | ||||
| } | ||||
| 
 | ||||
| ConfigureFilesystem::~ConfigureFilesystem() = default; | ||||
| 
 | ||||
| void ConfigureFilesystem::setConfiguration() { | ||||
|     ui->nand_directory_edit->setText( | ||||
|         QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); | ||||
|     ui->sdmc_directory_edit->setText( | ||||
|         QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | ||||
|     ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); | ||||
|     ui->dump_path_edit->setText( | ||||
|         QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); | ||||
|     ui->load_path_edit->setText( | ||||
|         QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); | ||||
|     ui->cache_directory_edit->setText( | ||||
|         QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); | ||||
| 
 | ||||
|     ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); | ||||
|     ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); | ||||
|     ui->dump_exefs->setChecked(Settings::values.dump_exefs); | ||||
|     ui->dump_nso->setChecked(Settings::values.dump_nso); | ||||
| 
 | ||||
|     ui->cache_game_list->setChecked(UISettings::values.cache_game_list); | ||||
| 
 | ||||
|     SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size); | ||||
|     SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size); | ||||
|     SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size); | ||||
|     SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size); | ||||
| 
 | ||||
|     UpdateEnabledControls(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureFilesystem::applyConfiguration() { | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, | ||||
|                           ui->nand_directory_edit->text().toStdString()); | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, | ||||
|                           ui->sdmc_directory_edit->text().toStdString()); | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString()); | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString()); | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, | ||||
|                           ui->cache_directory_edit->text().toStdString()); | ||||
|     Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString(); | ||||
| 
 | ||||
|     Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); | ||||
|     Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked(); | ||||
|     Settings::values.dump_exefs = ui->dump_exefs->isChecked(); | ||||
|     Settings::values.dump_nso = ui->dump_nso->isChecked(); | ||||
| 
 | ||||
|     UISettings::values.cache_game_list = ui->cache_game_list->isChecked(); | ||||
| 
 | ||||
|     Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( | ||||
|         ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong()); | ||||
|     Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||||
|         ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong()); | ||||
|     Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( | ||||
|         ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong()); | ||||
|     Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( | ||||
|         ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong()); | ||||
| } | ||||
| 
 | ||||
| void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) { | ||||
|     QString caption; | ||||
| 
 | ||||
|     switch (target) { | ||||
|     case DirectoryTarget::NAND: | ||||
|         caption = tr("Select Emulated NAND Directory..."); | ||||
|         break; | ||||
|     case DirectoryTarget::SD: | ||||
|         caption = tr("Select Emulated SD Directory..."); | ||||
|         break; | ||||
|     case DirectoryTarget::Gamecard: | ||||
|         caption = tr("Select Gamecard Path..."); | ||||
|         break; | ||||
|     case DirectoryTarget::Dump: | ||||
|         caption = tr("Select Dump Directory..."); | ||||
|         break; | ||||
|     case DirectoryTarget::Load: | ||||
|         caption = tr("Select Mod Load Directory..."); | ||||
|         break; | ||||
|     case DirectoryTarget::Cache: | ||||
|         caption = tr("Select Cache Directory..."); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     QString str; | ||||
|     if (target == DirectoryTarget::Gamecard) { | ||||
|         str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(), | ||||
|                                            QStringLiteral("NX Gamecard;*.xci")); | ||||
|     } else { | ||||
|         str = QFileDialog::getExistingDirectory(this, caption, edit->text()); | ||||
|     } | ||||
| 
 | ||||
|     if (str.isEmpty()) | ||||
|         return; | ||||
| 
 | ||||
|     edit->setText(str); | ||||
| } | ||||
| 
 | ||||
| void ConfigureFilesystem::ResetMetadata() { | ||||
|     if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + | ||||
|                           "game_list")) { | ||||
|         QMessageBox::information(this, tr("Reset Metadata Cache"), | ||||
|                                  tr("The metadata cache is already empty.")); | ||||
|     } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + | ||||
|                                               DIR_SEP + "game_list")) { | ||||
|         QMessageBox::information(this, tr("Reset Metadata Cache"), | ||||
|                                  tr("The operation completed successfully.")); | ||||
|         UISettings::values.is_game_list_reload_pending.exchange(true); | ||||
|     } else { | ||||
|         QMessageBox::warning( | ||||
|             this, tr("Reset Metadata Cache"), | ||||
|             tr("The metadata cache couldn't be deleted. It might be in use or non-existent.")); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ConfigureFilesystem::UpdateEnabledControls() { | ||||
|     ui->gamecard_current_game->setEnabled(ui->gamecard_inserted->isChecked()); | ||||
|     ui->gamecard_path_edit->setEnabled(ui->gamecard_inserted->isChecked() && | ||||
|                                        !ui->gamecard_current_game->isChecked()); | ||||
|     ui->gamecard_path_button->setEnabled(ui->gamecard_inserted->isChecked() && | ||||
|                                          !ui->gamecard_current_game->isChecked()); | ||||
| } | ||||
| 
 | ||||
| void ConfigureFilesystem::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/yuzu/configuration/configure_filesystem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/yuzu/configuration/configure_filesystem.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| // Copyright 2019 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <QWidget> | ||||
| 
 | ||||
| class QLineEdit; | ||||
| 
 | ||||
| namespace Ui { | ||||
| class ConfigureFilesystem; | ||||
| } | ||||
| 
 | ||||
| class ConfigureFilesystem : public QWidget { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit ConfigureFilesystem(QWidget* parent = nullptr); | ||||
|     ~ConfigureFilesystem() override; | ||||
| 
 | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| private: | ||||
|     void setConfiguration(); | ||||
| 
 | ||||
|     enum class DirectoryTarget { | ||||
|         NAND, | ||||
|         SD, | ||||
|         Gamecard, | ||||
|         Dump, | ||||
|         Load, | ||||
|         Cache, | ||||
|     }; | ||||
| 
 | ||||
|     void SetDirectory(DirectoryTarget target, QLineEdit* edit); | ||||
|     void ResetMetadata(); | ||||
|     void UpdateEnabledControls(); | ||||
| 
 | ||||
|     std::unique_ptr<Ui::ConfigureFilesystem> ui; | ||||
| }; | ||||
							
								
								
									
										395
									
								
								src/yuzu/configuration/configure_filesystem.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								src/yuzu/configuration/configure_filesystem.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,395 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>ConfigureFilesystem</class> | ||||
|  <widget class="QWidget" name="ConfigureFilesystem"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>453</width> | ||||
|     <height>561</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Form</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="groupBox"> | ||||
|        <property name="title"> | ||||
|         <string>Storage Directories</string> | ||||
|        </property> | ||||
|        <layout class="QGridLayout" name="gridLayout"> | ||||
|         <item row="0" column="0"> | ||||
|          <widget class="QLabel" name="label"> | ||||
|           <property name="text"> | ||||
|            <string>NAND</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="3"> | ||||
|          <widget class="QToolButton" name="nand_directory_button"> | ||||
|           <property name="text"> | ||||
|            <string>...</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="2"> | ||||
|          <widget class="QLineEdit" name="nand_directory_edit"/> | ||||
|         </item> | ||||
|         <item row="1" column="2"> | ||||
|          <widget class="QLineEdit" name="sdmc_directory_edit"/> | ||||
|         </item> | ||||
|         <item row="1" column="0"> | ||||
|          <widget class="QLabel" name="label_2"> | ||||
|           <property name="text"> | ||||
|            <string>SD Card</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="1" column="3"> | ||||
|          <widget class="QToolButton" name="sdmc_directory_button"> | ||||
|           <property name="text"> | ||||
|            <string>...</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="1"> | ||||
|          <spacer name="horizontalSpacer"> | ||||
|           <property name="orientation"> | ||||
|            <enum>Qt::Horizontal</enum> | ||||
|           </property> | ||||
|           <property name="sizeType"> | ||||
|            <enum>QSizePolicy::Maximum</enum> | ||||
|           </property> | ||||
|           <property name="sizeHint" stdset="0"> | ||||
|            <size> | ||||
|             <width>60</width> | ||||
|             <height>20</height> | ||||
|            </size> | ||||
|           </property> | ||||
|          </spacer> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="groupBox_2"> | ||||
|        <property name="title"> | ||||
|         <string>Gamecard</string> | ||||
|        </property> | ||||
|        <layout class="QGridLayout" name="gridLayout_2"> | ||||
|         <item row="2" column="1"> | ||||
|          <widget class="QLabel" name="label_3"> | ||||
|           <property name="text"> | ||||
|            <string>Path</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="2" column="2"> | ||||
|          <widget class="QLineEdit" name="gamecard_path_edit"/> | ||||
|         </item> | ||||
|         <item row="0" column="1"> | ||||
|          <widget class="QCheckBox" name="gamecard_inserted"> | ||||
|           <property name="text"> | ||||
|            <string>Inserted</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="1" column="1"> | ||||
|          <widget class="QCheckBox" name="gamecard_current_game"> | ||||
|           <property name="text"> | ||||
|            <string>Current Game</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="2" column="3"> | ||||
|          <widget class="QToolButton" name="gamecard_path_button"> | ||||
|           <property name="text"> | ||||
|            <string>...</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="groupBox_3"> | ||||
|        <property name="title"> | ||||
|         <string>Storage Sizes</string> | ||||
|        </property> | ||||
|        <layout class="QGridLayout" name="gridLayout_3"> | ||||
|         <item row="3" column="0"> | ||||
|          <widget class="QLabel" name="label_5"> | ||||
|           <property name="text"> | ||||
|            <string>SD Card</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="1" column="0"> | ||||
|          <widget class="QLabel" name="label_4"> | ||||
|           <property name="text"> | ||||
|            <string>System NAND</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="1" column="1"> | ||||
|          <widget class="QComboBox" name="sysnand_size"> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>2.5 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="3" column="1"> | ||||
|          <widget class="QComboBox" name="sdmc_size"> | ||||
|           <property name="currentText"> | ||||
|            <string>32 GB</string> | ||||
|           </property> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>1 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>2 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>4 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>8 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>16 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>32 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>64 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>128 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>256 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>1 TB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="2" column="1"> | ||||
|          <widget class="QComboBox" name="usrnand_size"> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>26 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="2" column="0"> | ||||
|          <widget class="QLabel" name="label_6"> | ||||
|           <property name="text"> | ||||
|            <string>User NAND</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="0"> | ||||
|          <widget class="QLabel" name="label_7"> | ||||
|           <property name="text"> | ||||
|            <string>NAND</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="1"> | ||||
|          <widget class="QComboBox" name="nand_size"> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string>29.1 GB</string> | ||||
|            </property> | ||||
|           </item> | ||||
|          </widget> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="groupBox_4"> | ||||
|        <property name="title"> | ||||
|         <string>Patch Manager</string> | ||||
|        </property> | ||||
|        <layout class="QGridLayout" name="gridLayout_4"> | ||||
|         <item row="1" column="2"> | ||||
|          <widget class="QLineEdit" name="load_path_edit"/> | ||||
|         </item> | ||||
|         <item row="0" column="2"> | ||||
|          <widget class="QLineEdit" name="dump_path_edit"/> | ||||
|         </item> | ||||
|         <item row="0" column="3"> | ||||
|          <widget class="QToolButton" name="dump_path_button"> | ||||
|           <property name="text"> | ||||
|            <string>...</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="1" column="3"> | ||||
|          <widget class="QToolButton" name="load_path_button"> | ||||
|           <property name="text"> | ||||
|            <string>...</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="2" column="0" colspan="4"> | ||||
|          <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="dump_nso"> | ||||
|             <property name="text"> | ||||
|              <string>Dump Decompressed NSOs</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="dump_exefs"> | ||||
|             <property name="text"> | ||||
|              <string>Dump ExeFS</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </item> | ||||
|         <item row="1" column="0"> | ||||
|          <widget class="QLabel" name="label_9"> | ||||
|           <property name="text"> | ||||
|            <string>Mod Load Root</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="0"> | ||||
|          <widget class="QLabel" name="label_8"> | ||||
|           <property name="text"> | ||||
|            <string>Dump Root</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="1"> | ||||
|          <spacer name="horizontalSpacer_2"> | ||||
|           <property name="orientation"> | ||||
|            <enum>Qt::Horizontal</enum> | ||||
|           </property> | ||||
|           <property name="sizeType"> | ||||
|            <enum>QSizePolicy::Fixed</enum> | ||||
|           </property> | ||||
|           <property name="sizeHint" stdset="0"> | ||||
|            <size> | ||||
|             <width>40</width> | ||||
|             <height>20</height> | ||||
|            </size> | ||||
|           </property> | ||||
|          </spacer> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="groupBox_5"> | ||||
|        <property name="title"> | ||||
|         <string>Caching</string> | ||||
|        </property> | ||||
|        <layout class="QGridLayout" name="gridLayout_5"> | ||||
|         <item row="0" column="0"> | ||||
|          <widget class="QLabel" name="label_10"> | ||||
|           <property name="text"> | ||||
|            <string>Cache Directory</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="1"> | ||||
|          <spacer name="horizontalSpacer_3"> | ||||
|           <property name="orientation"> | ||||
|            <enum>Qt::Horizontal</enum> | ||||
|           </property> | ||||
|           <property name="sizeType"> | ||||
|            <enum>QSizePolicy::Fixed</enum> | ||||
|           </property> | ||||
|           <property name="sizeHint" stdset="0"> | ||||
|            <size> | ||||
|             <width>40</width> | ||||
|             <height>20</height> | ||||
|            </size> | ||||
|           </property> | ||||
|          </spacer> | ||||
|         </item> | ||||
|         <item row="0" column="2"> | ||||
|          <widget class="QLineEdit" name="cache_directory_edit"/> | ||||
|         </item> | ||||
|         <item row="0" column="3"> | ||||
|          <widget class="QToolButton" name="cache_directory_button"> | ||||
|           <property name="text"> | ||||
|            <string>...</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="1" column="0" colspan="4"> | ||||
|          <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="cache_game_list"> | ||||
|             <property name="text"> | ||||
|              <string>Cache Game List Metadata</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QPushButton" name="reset_game_list_cache"> | ||||
|             <property name="text"> | ||||
|              <string>Reset Metadata Cache</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <spacer name="verticalSpacer"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Vertical</enum> | ||||
|      </property> | ||||
|      <property name="sizeHint" stdset="0"> | ||||
|       <size> | ||||
|        <width>20</width> | ||||
|        <height>40</height> | ||||
|       </size> | ||||
|      </property> | ||||
|     </spacer> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
| @ -2,6 +2,7 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QSpinBox> | ||||
| #include "core/core.h" | ||||
| #include "core/settings.h" | ||||
| #include "ui_configure_general.h" | ||||
|  | ||||
| @ -221,7 +221,7 @@ GMainWindow::GMainWindow() | ||||
|         std::make_unique<FileSys::ContentProviderUnion>()); | ||||
|     Core::System::GetInstance().RegisterContentProvider( | ||||
|         FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); | ||||
|     Service::FileSystem::CreateFactories(*vfs); | ||||
|     Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); | ||||
| 
 | ||||
|     // Gen keys if necessary
 | ||||
|     OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); | ||||
| @ -1507,15 +1507,19 @@ void GMainWindow::OnMenuInstallToNAND() { | ||||
|             failed(); | ||||
|             return; | ||||
|         } | ||||
|         const auto res = | ||||
|             Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy); | ||||
|         const auto res = Core::System::GetInstance() | ||||
|                              .GetFileSystemController() | ||||
|                              .GetUserNANDContents() | ||||
|                              ->InstallEntry(*nsp, false, qt_raw_copy); | ||||
|         if (res == FileSys::InstallResult::Success) { | ||||
|             success(); | ||||
|         } else { | ||||
|             if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||||
|                 if (overwrite()) { | ||||
|                     const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( | ||||
|                         *nsp, true, qt_raw_copy); | ||||
|                     const auto res2 = Core::System::GetInstance() | ||||
|                                           .GetFileSystemController() | ||||
|                                           .GetUserNANDContents() | ||||
|                                           ->InstallEntry(*nsp, true, qt_raw_copy); | ||||
|                     if (res2 == FileSys::InstallResult::Success) { | ||||
|                         success(); | ||||
|                     } else { | ||||
| @ -1569,19 +1573,28 @@ void GMainWindow::OnMenuInstallToNAND() { | ||||
| 
 | ||||
|         FileSys::InstallResult res; | ||||
|         if (index >= static_cast<size_t>(FileSys::TitleType::Application)) { | ||||
|             res = Service::FileSystem::GetUserNANDContents()->InstallEntry( | ||||
|                 *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); | ||||
|             res = Core::System::GetInstance() | ||||
|                       .GetFileSystemController() | ||||
|                       .GetUserNANDContents() | ||||
|                       ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||||
|                                      qt_raw_copy); | ||||
|         } else { | ||||
|             res = Service::FileSystem::GetSystemNANDContents()->InstallEntry( | ||||
|                 *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); | ||||
|             res = Core::System::GetInstance() | ||||
|                       .GetFileSystemController() | ||||
|                       .GetSystemNANDContents() | ||||
|                       ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||||
|                                      qt_raw_copy); | ||||
|         } | ||||
| 
 | ||||
|         if (res == FileSys::InstallResult::Success) { | ||||
|             success(); | ||||
|         } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||||
|             if (overwrite()) { | ||||
|                 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( | ||||
|                     *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); | ||||
|                 const auto res2 = Core::System::GetInstance() | ||||
|                                       .GetFileSystemController() | ||||
|                                       .GetUserNANDContents() | ||||
|                                       ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), | ||||
|                                                      true, qt_raw_copy); | ||||
|                 if (res2 == FileSys::InstallResult::Success) { | ||||
|                     success(); | ||||
|                 } else { | ||||
| @ -1611,7 +1624,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) | ||||
|         FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir | ||||
|                                                                       : FileUtil::UserPath::NANDDir, | ||||
|                               dir_path.toStdString()); | ||||
|         Service::FileSystem::CreateFactories(*vfs); | ||||
|         Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); | ||||
|         game_list->PopulateAsync(UISettings::values.game_dirs); | ||||
|     } | ||||
| } | ||||
| @ -1996,7 +2009,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | ||||
| 
 | ||||
|         const auto function = [this, &keys, &pdm] { | ||||
|             keys.PopulateFromPartitionData(pdm); | ||||
|             Service::FileSystem::CreateFactories(*vfs); | ||||
|             Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); | ||||
|             keys.DeriveETicket(pdm); | ||||
|         }; | ||||
| 
 | ||||
| @ -2041,7 +2054,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | ||||
|         prog.close(); | ||||
|     } | ||||
| 
 | ||||
|     Service::FileSystem::CreateFactories(*vfs); | ||||
|     Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); | ||||
| 
 | ||||
|     if (behavior == ReinitializeKeyBehavior::Warning) { | ||||
|         game_list->PopulateAsync(UISettings::values.game_dirs); | ||||
|  | ||||
| @ -316,6 +316,29 @@ void Config::ReadValues() { | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, | ||||
|                           sdl2_config->Get("Data Storage", "sdmc_directory", | ||||
|                                            FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, | ||||
|                           sdl2_config->Get("Data Storage", "load_directory", | ||||
|                                            FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, | ||||
|                           sdl2_config->Get("Data Storage", "dump_directory", | ||||
|                                            FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); | ||||
|     FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, | ||||
|                           sdl2_config->Get("Data Storage", "cache_directory", | ||||
|                                            FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); | ||||
|     Settings::values.gamecard_inserted = | ||||
|         sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); | ||||
|     Settings::values.gamecard_current_game = | ||||
|         sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false); | ||||
|     Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); | ||||
|     Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger( | ||||
|         "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB))); | ||||
|     Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger( | ||||
|         "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB))); | ||||
|     Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||||
|         sdl2_config->GetInteger("Data Storage", "nand_system_size", | ||||
|                                 static_cast<long>(Settings::NANDSystemSize::S2_5GB))); | ||||
|     Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger( | ||||
|         "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB))); | ||||
| 
 | ||||
|     // System
 | ||||
|     Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | ||||
|  | ||||
| @ -173,6 +173,20 @@ volume = | ||||
| # 1 (default): Yes, 0: No | ||||
| use_virtual_sd = | ||||
| 
 | ||||
| # Whether or not to enable gamecard emulation | ||||
| # 1: Yes, 0 (default): No | ||||
| gamecard_inserted = | ||||
| 
 | ||||
| # Whether or not the gamecard should be emulated as the current game | ||||
| # If 'gamecard_inserted' is 0 this setting is irrelevant | ||||
| # 1: Yes, 0 (default): No | ||||
| gamecard_current_game = | ||||
| 
 | ||||
| # Path to an XCI file to use as the gamecard | ||||
| # If 'gamecard_inserted' is 0 this setting is irrelevant | ||||
| # If 'gamecard_current_game' is 1 this setting is irrelevant | ||||
| gamecard_path = | ||||
| 
 | ||||
| [System] | ||||
| # Whether the system is docked | ||||
| # 1: Yes, 0 (default): No | ||||
|  | ||||
| @ -184,7 +184,7 @@ int main(int argc, char** argv) { | ||||
|     Core::System& system{Core::System::GetInstance()}; | ||||
|     system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||||
|     system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | ||||
|     Service::FileSystem::CreateFactories(*system.GetFilesystem()); | ||||
|     system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | ||||
| 
 | ||||
|     SCOPE_EXIT({ system.Shutdown(); }); | ||||
| 
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| #include "common/telemetry.h" | ||||
| #include "core/core.h" | ||||
| #include "core/crypto/key_manager.h" | ||||
| #include "core/file_sys/registered_cache.h" | ||||
| #include "core/file_sys/vfs_real.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/loader/loader.h" | ||||
| @ -216,8 +217,9 @@ int main(int argc, char** argv) { | ||||
|     }; | ||||
| 
 | ||||
|     Core::System& system{Core::System::GetInstance()}; | ||||
|     system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||||
|     system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | ||||
|     Service::FileSystem::CreateFactories(*system.GetFilesystem()); | ||||
|     system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | ||||
| 
 | ||||
|     SCOPE_EXIT({ system.Shutdown(); }); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 David
						David