diff options
Diffstat (limited to 'src/core/file_sys')
| -rw-r--r-- | src/core/file_sys/content_archive.cpp | 4 | ||||
| -rw-r--r-- | src/core/file_sys/content_archive.h | 5 | ||||
| -rw-r--r-- | src/core/file_sys/fssystem/fssystem_indirect_storage.cpp | 13 | ||||
| -rw-r--r-- | src/core/file_sys/fssystem/fssystem_indirect_storage.h | 12 | ||||
| -rw-r--r-- | src/core/file_sys/nca_metadata.h | 4 | ||||
| -rw-r--r-- | src/core/file_sys/registered_cache.cpp | 35 | ||||
| -rw-r--r-- | src/core/file_sys/submission_package.cpp | 14 |
7 files changed, 71 insertions, 16 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 2b478d6b99..b47b9437d0 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -29,7 +29,7 @@ static u8 MasterKeyIdForKeyGeneration(u8 key_generation) { return std::max<u8>(key_generation, 1) - 1; } -NCA::NCA(VirtualFile file_, const NCA* base_nca) +NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base) : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { if (file == nullptr) { status = Loader::ResultStatus::ErrorNullFile; @@ -110,7 +110,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) } } - if (is_update && base_nca == nullptr) { + if (is_update && base_nca == nullptr && !allow_missing_base) { status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS; } else { status = Loader::ResultStatus::Success; diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 8560617f5b..b88fcdc4ee 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -63,7 +66,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) { // After construction, use GetStatus to determine if the file is valid and ready to be used. class NCA : public ReadOnlyVfsDirectory { public: - explicit NCA(VirtualFile file, const NCA* base_nca = nullptr); + explicit NCA(VirtualFile file, const NCA* base_nca = nullptr, bool allow_missing_base = false); ~NCA() override; Loader::ResultStatus GetStatus() const; diff --git a/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp b/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp index 41f5b75b6e..e66ce49d9a 100644 --- a/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/logging.h" #include "core/file_sys/errors.h" #include "core/file_sys/fssystem/fssystem_indirect_storage.h" @@ -99,6 +100,18 @@ Result IndirectStorage::GetEntryList(Entry* out_entries, s32* out_entry_count, s R_SUCCEED(); } +void IndirectStorage::ReportMissingOriginal(s64 offset, s64 size) { + if (m_reported_missing_original) { + return; + } + m_reported_missing_original = true; + + LOG_ERROR(Common_Filesystem, + "Patch storage requests {:#x} bytes at {:#x} from a base storage that is not " + "present; this data will read as garbage", + size, offset); +} + size_t IndirectStorage::Read(u8* buffer, size_t size, size_t offset) const { // Validate pre-conditions. ASSERT(this->IsInitialized()); diff --git a/src/core/file_sys/fssystem/fssystem_indirect_storage.h b/src/core/file_sys/fssystem/fssystem_indirect_storage.h index cc7b5cd718..81f94e2e19 100644 --- a/src/core/file_sys/fssystem/fssystem_indirect_storage.h +++ b/src/core/file_sys/fssystem/fssystem_indirect_storage.h @@ -85,6 +85,9 @@ public: void SetStorage(s32 idx, VirtualFile storage) { ASSERT(0 <= idx && idx < StorageCount); m_data_storage[idx] = storage; + if (idx == 0) { + m_original_missing = storage == nullptr || storage->GetSize() == 0; + } } template <typename T> @@ -130,6 +133,9 @@ protected: template <bool ContinuousCheck, bool RangeCheck, typename F> Result OperatePerEntry(s64 offset, s64 size, F func); + // Launching another game makes the original storage inaccessable. + // This is a helper for multi-nca games. + void ReportMissingOriginal(s64 offset, s64 size); private: struct ContinuousReadingEntry { @@ -154,6 +160,8 @@ private: private: mutable BucketTree m_table; std::array<VirtualFile, StorageCount> m_data_storage; + bool m_original_missing{false}; + bool m_reported_missing_original{false}; }; template <bool ContinuousCheck, bool RangeCheck, typename F> @@ -272,6 +280,10 @@ Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) { if (needs_operate) { const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset(); + if (cur_entry.storage_index == 0 && m_original_missing) { + this->ReportMissingOriginal(cur_offset, cur_size); + } + if constexpr (RangeCheck) { // Get the current data storage's size. s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize(); diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 43437d0dd7..07aae1642e 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -48,11 +48,11 @@ struct ContentRecord { std::array<u8, 0x10> nca_id; std::array<u8, 0x6> size; ContentRecordType type; - INSERT_PADDING_BYTES(1); + u8 id_offset; }; static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size."); -constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}}; +constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, 0}; struct MetaRecord { u64_le title_id; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 9e9db0ed4d..83f5e265dd 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -567,20 +567,45 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { } static std::optional<NcaID> CheckMapForContentRecord(const ankerl::unordered_dense::map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { - const auto cmnt_iter = map.find(title_id); + auto cmnt_iter = map.find(title_id); + u8 id_offset = 0; + if (cmnt_iter == map.cend()) { - return std::nullopt; + const auto program_index = title_id & AOC_TITLE_ID_MASK; + if (program_index == 0) { + return std::nullopt; + } + + cmnt_iter = map.find(title_id & ~AOC_TITLE_ID_MASK); + if (cmnt_iter == map.cend()) { + return std::nullopt; + } + + id_offset = static_cast<u8>(program_index); } const auto& cnmt = cmnt_iter->second; const auto& content_records = cnmt.GetContentRecords(); const auto iter = std::find_if(content_records.cbegin(), content_records.cend(), - [type](const ContentRecord& rec) { return rec.type == type; }); - if (iter == content_records.cend()) { + [type, id_offset](const ContentRecord& rec) { + return rec.type == type && rec.id_offset == id_offset; + }); + if (iter != content_records.cend()) { + return std::make_optional(iter->nca_id); + } + + if (id_offset != 0) { + return std::nullopt; + } + + const auto fallback_iter = + std::find_if(content_records.cbegin(), content_records.cend(), + [type](const ContentRecord& rec) { return rec.type == type; }); + if (fallback_iter == content_records.cend()) { return std::nullopt; } - return std::make_optional(iter->nca_id); + return std::make_optional(fallback_iter->nca_id); } std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id, diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 630bb0b079..0ea323492b 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -12,6 +12,7 @@ #include "common/hex_util.h" #include "common/logging.h" #include "core/crypto/key_manager.h" +#include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/partition_filesystem.h" @@ -65,10 +66,12 @@ u64 NSP::GetProgramTitleID() const { } auto program_id = expected_program_id; - if (program_id == 0) { - if (!program_status.empty()) { - program_id = program_status.begin()->first; - } + if (program_id == 0 && !program_status.empty()) { + program_id = std::min_element(program_status.cbegin(), program_status.cend(), + [](const auto& lhs, const auto& rhs) { + return lhs.first < rhs.first; + }) + ->first; } program_id = program_id + program_index; @@ -273,8 +276,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { // If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and // 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular // update NCA. - if ((next_nca->GetTitleId() & 0x7FF) != 0 && - (next_nca->GetTitleId() & 0x800) == 0) { + if ((next_nca->GetTitleId() & AOC_TITLE_ID_MASK) != 0) { ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca); } else { |
