diff options
Diffstat (limited to 'src/core/file_sys')
-rw-r--r-- | src/core/file_sys/bis_factory.cpp | 23 | ||||
-rw-r--r-- | src/core/file_sys/registered_cache.cpp | 98 | ||||
-rw-r--r-- | src/core/file_sys/registered_cache.h | 4 | ||||
-rw-r--r-- | src/core/file_sys/sdmc_factory.cpp | 4 | ||||
-rw-r--r-- | src/core/file_sys/vfs_real.cpp | 27 |
5 files changed, 125 insertions, 31 deletions
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 8935a62c3..285277ef8 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -12,6 +12,10 @@ namespace FileSys { +constexpr u64 NAND_USER_SIZE = 0x680000000; // 26624 MiB +constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB +constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB + BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_) : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), dump_root(std::move(dump_root_)), @@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const { u64 BISFactory::GetSystemNANDFreeSpace() const { const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system"); - if (sys_dir == nullptr) - return 0; + if (sys_dir == nullptr) { + return GetSystemNANDTotalSpace(); + } return GetSystemNANDTotalSpace() - sys_dir->GetSize(); } u64 BISFactory::GetSystemNANDTotalSpace() const { - return static_cast<u64>(Settings::values.nand_system_size); + return 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(); + // For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes. + // Set the free space to be 1 MiB less than the total as a workaround to this issue. + return GetUserNANDTotalSpace() - 0x100000; } u64 BISFactory::GetUserNANDTotalSpace() const { - return static_cast<u64>(Settings::values.nand_user_size); + return NAND_USER_SIZE; } u64 BISFactory::GetFullNANDTotalSpace() const { - return static_cast<u64>(Settings::values.nand_total_size); + return NAND_TOTAL_SIZE; } VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const { diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 27c1b0233..37351c561 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -547,6 +547,56 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); } +bool RegisteredCache::RemoveExistingEntry(u64 title_id) { + const auto delete_nca = [this](const NcaID& id) { + const auto path = GetRelativePathFromNcaID(id, false, true, false); + + if (dir->GetFileRelative(path) == nullptr) { + return false; + } + + Core::Crypto::SHA256Hash hash{}; + mbedtls_sha256_ret(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; + }; + + // If an entry exists in the registered cache, remove it + if (HasEntry(title_id, ContentRecordType::Meta)) { + LOG_INFO(Loader, + "Previously installed entry (v{}) for title_id={:016X} detected! " + "Attempting to remove...", + GetEntryVersion(title_id).value_or(0), title_id); + // Get all the ncas associated with the current CNMT and delete them + const auto meta_old_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); + const auto program_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); + const auto data_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); + const auto control_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); + const auto html_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); + const auto legal_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); + + delete_nca(meta_old_id); + delete_nca(program_id); + delete_nca(data_id); + delete_nca(control_id); + delete_nca(html_id); + delete_nca(legal_id); + return true; + } + return false; +} + InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, const VfsCopyFunction& copy) { const auto ncas = nsp.GetNCAsCollapsed(); @@ -560,31 +610,57 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex return InstallResult::ErrorMetaFailed; } - // Install Metadata File const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32); const auto meta_id = Common::HexStringToArray<16>(meta_id_raw); - const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id); - if (res != InstallResult::Success) - return res; + if ((*meta_iter)->GetSubdirectories().empty()) { + LOG_ERROR(Loader, + "The file you are attempting to install does not contain a section0 within the " + "metadata NCA and is therefore malformed. Verify that the file is valid."); + return InstallResult::ErrorMetaFailed; + } - // Install all the other NCAs const auto section0 = (*meta_iter)->GetSubdirectories()[0]; + + if (section0->GetFiles().empty()) { + LOG_ERROR(Loader, + "The file you are attempting to install does not contain a CNMT within the " + "metadata NCA and is therefore malformed. Verify that the file is valid."); + return InstallResult::ErrorMetaFailed; + } + const auto cnmt_file = section0->GetFiles()[0]; const CNMT cnmt(cnmt_file); + + const auto title_id = cnmt.GetTitleID(); + const auto result = RemoveExistingEntry(title_id); + + // Install Metadata File + const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id); + if (res != InstallResult::Success) { + return res; + } + + // Install all the other NCAs for (const auto& record : cnmt.GetContentRecords()) { // Ignore DeltaFragments, they are not useful to us - if (record.type == ContentRecordType::DeltaFragment) + if (record.type == ContentRecordType::DeltaFragment) { continue; + } const auto nca = GetNCAFromNSPForID(nsp, record.nca_id); - if (nca == nullptr) + if (nca == nullptr) { return InstallResult::ErrorCopyFailed; + } const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id); - if (res2 != InstallResult::Success) + if (res2 != InstallResult::Success) { return res2; + } } Refresh(); + if (result) { + return InstallResult::OverwriteExisting; + } return InstallResult::Success; } @@ -610,8 +686,9 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); memcpy(&c_rec.nca_id, &c_rec.hash, 16); const CNMT new_cnmt(header, opt_header, {c_rec}, {}); - if (!RawInstallYuzuMeta(new_cnmt)) + if (!RawInstallYuzuMeta(new_cnmt)) { return InstallResult::ErrorMetaFailed; + } return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); } @@ -649,8 +726,9 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti } auto out = dir->CreateFileRelative(path); - if (out == nullptr) + if (out == nullptr) { return InstallResult::ErrorCopyFailed; + } return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success : InstallResult::ErrorCopyFailed; } diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index f339cd17b..29cf0d40c 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -34,6 +34,7 @@ using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile enum class InstallResult { Success, + OverwriteExisting, ErrorAlreadyExists, ErrorCopyFailed, ErrorMetaFailed, @@ -154,6 +155,9 @@ public: std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, std::optional<u64> title_id = {}) const override; + // Removes an existing entry based on title id + bool RemoveExistingEntry(u64 title_id); + // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure // there is a meta NCA and all of them are accessible. InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index 5113a1ca6..6f732e4d8 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -10,6 +10,8 @@ namespace FileSys { +constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB + SDMCFactory::SDMCFactory(VirtualDir dir_) : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>( GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), @@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const { } u64 SDMCFactory::GetSDMCTotalSpace() const { - return static_cast<u64>(Settings::values.sdmc_size); + return SDMC_TOTAL_SIZE; } } // namespace FileSys diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index e21300a7c..96ce5957c 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -112,19 +112,26 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ const auto new_path = FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); - if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || - FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) - return nullptr; - if (cache.find(old_path) != cache.end()) { - auto cached = cache[old_path]; - if (!cached.expired()) { - auto file = cached.lock(); - file->Open(new_path, "r+b"); - cache.erase(old_path); - cache[new_path] = file; + auto file = cache[old_path].lock(); + + if (!cache[old_path].expired()) { + file->Close(); + } + + if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || + FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) { + return nullptr; } + + cache.erase(old_path); + file->Open(new_path, "r+b"); + cache[new_path] = file; + } else { + UNREACHABLE(); + return nullptr; } + return OpenFile(new_path, Mode::ReadWrite); } |