mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-11-06 06:51:59 +01:00
Speed up NCZBLOCK reads
This commit is contained in:
parent
44cd950747
commit
465b95f353
5 changed files with 135 additions and 80 deletions
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
|
@ -93,6 +93,11 @@ Result NcaFileSystemDriver::OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader*
|
|||
// Initialize our header reader for the fs index.
|
||||
R_TRY(out_header_reader->Initialize(*m_reader, fs_index));
|
||||
|
||||
// NCZ files serve already-decrypted body data (CTR sections are stored decrypted).
|
||||
// Skip the AES layers so we don't decrypt plaintext twice.
|
||||
const bool is_pre_decrypted =
|
||||
m_reader->GetStorage() && m_reader->GetStorage()->HasDecryptedSections();
|
||||
|
||||
// Declare the storage we're opening.
|
||||
VirtualFile storage;
|
||||
|
||||
|
|
@ -158,7 +163,7 @@ Result NcaFileSystemDriver::OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader*
|
|||
out_header_reader->GetPatchMetaDataHashDataInfo()));
|
||||
}
|
||||
|
||||
if (patch_info.HasAesCtrExTable()) {
|
||||
if (patch_info.HasAesCtrExTable() && !is_pre_decrypted) {
|
||||
// Check the encryption type.
|
||||
ASSERT(out_header_reader->GetEncryptionType() == NcaFsHeader::EncryptionType::None ||
|
||||
out_header_reader->GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrEx ||
|
||||
|
|
@ -196,32 +201,34 @@ Result NcaFileSystemDriver::OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader*
|
|||
}
|
||||
} else {
|
||||
// Create the appropriate storage for the encryption type.
|
||||
switch (out_header_reader->GetEncryptionType()) {
|
||||
case NcaFsHeader::EncryptionType::None:
|
||||
// If there's no encryption, use the base storage we made previously.
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesXts:
|
||||
R_TRY(this->CreateAesXtsStorage(std::addressof(storage), std::move(storage),
|
||||
fs_data_offset));
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesCtr:
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage),
|
||||
fs_data_offset, out_header_reader->GetAesCtrUpperIv(),
|
||||
AlignmentStorageRequirement::None));
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesCtrSkipLayerHash: {
|
||||
// Create the aes ctr storage.
|
||||
VirtualFile aes_ctr_storage;
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(aes_ctr_storage), storage,
|
||||
fs_data_offset, out_header_reader->GetAesCtrUpperIv(),
|
||||
AlignmentStorageRequirement::None));
|
||||
if (!is_pre_decrypted) {
|
||||
switch (out_header_reader->GetEncryptionType()) {
|
||||
case NcaFsHeader::EncryptionType::None:
|
||||
// If there's no encryption, use the base storage we made previously.
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesXts:
|
||||
R_TRY(this->CreateAesXtsStorage(std::addressof(storage), std::move(storage),
|
||||
fs_data_offset));
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesCtr:
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage),
|
||||
fs_data_offset, out_header_reader->GetAesCtrUpperIv(),
|
||||
AlignmentStorageRequirement::None));
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesCtrSkipLayerHash: {
|
||||
// Create the aes ctr storage.
|
||||
VirtualFile aes_ctr_storage;
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(aes_ctr_storage), storage,
|
||||
fs_data_offset, out_header_reader->GetAesCtrUpperIv(),
|
||||
AlignmentStorageRequirement::None));
|
||||
|
||||
// Create region switch storage.
|
||||
R_TRY(this->CreateRegionSwitchStorage(std::addressof(storage), out_header_reader,
|
||||
std::move(storage), std::move(aes_ctr_storage)));
|
||||
} break;
|
||||
default:
|
||||
R_THROW(ResultInvalidNcaFsHeaderEncryptionType);
|
||||
// Create region switch storage.
|
||||
R_TRY(this->CreateRegionSwitchStorage(std::addressof(storage), out_header_reader,
|
||||
std::move(storage), std::move(aes_ctr_storage)));
|
||||
} break;
|
||||
default:
|
||||
R_THROW(ResultInvalidNcaFsHeaderEncryptionType);
|
||||
}
|
||||
}
|
||||
|
||||
// Potentially save storages to our context.
|
||||
|
|
@ -382,21 +389,25 @@ Result NcaFileSystemDriver::OpenIndirectableStorageAsOriginal(
|
|||
}
|
||||
|
||||
// Create the appropriate storage for the encryption type.
|
||||
switch (header_reader->GetEncryptionType()) {
|
||||
case NcaFsHeader::EncryptionType::None:
|
||||
// If there's no encryption, use the base storage we made previously.
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesXts:
|
||||
R_TRY(
|
||||
this->CreateAesXtsStorage(std::addressof(storage), std::move(storage), fs_data_offset));
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesCtr:
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage), fs_data_offset,
|
||||
header_reader->GetAesCtrUpperIv(),
|
||||
AlignmentStorageRequirement::CacheBlockSize));
|
||||
break;
|
||||
default:
|
||||
R_THROW(ResultInvalidNcaFsHeaderEncryptionType);
|
||||
const bool is_pre_decrypted =
|
||||
m_reader->GetStorage() && m_reader->GetStorage()->HasDecryptedSections();
|
||||
if (!is_pre_decrypted) {
|
||||
switch (header_reader->GetEncryptionType()) {
|
||||
case NcaFsHeader::EncryptionType::None:
|
||||
// If there's no encryption, use the base storage we made previously.
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesXts:
|
||||
R_TRY(this->CreateAesXtsStorage(std::addressof(storage), std::move(storage),
|
||||
fs_data_offset));
|
||||
break;
|
||||
case NcaFsHeader::EncryptionType::AesCtr:
|
||||
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage),
|
||||
fs_data_offset, header_reader->GetAesCtrUpperIv(),
|
||||
AlignmentStorageRequirement::CacheBlockSize));
|
||||
break;
|
||||
default:
|
||||
R_THROW(ResultInvalidNcaFsHeaderEncryptionType);
|
||||
}
|
||||
}
|
||||
|
||||
// Set output storage.
|
||||
|
|
@ -432,6 +443,11 @@ Result NcaFileSystemDriver::CreateAesCtrStorage(
|
|||
ASSERT(out != nullptr);
|
||||
ASSERT(base_storage != nullptr);
|
||||
|
||||
if (m_reader->GetStorage() && m_reader->GetStorage()->HasDecryptedSections()) {
|
||||
*out = std::move(base_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// Create the iv.
|
||||
std::array<u8, AesCtrStorage::IvSize> iv{};
|
||||
AesCtrStorage::MakeIv(iv.data(), sizeof(iv), upper_iv.value, offset);
|
||||
|
|
@ -469,6 +485,11 @@ Result NcaFileSystemDriver::CreateAesXtsStorage(VirtualFile* out, VirtualFile ba
|
|||
ASSERT(out != nullptr);
|
||||
ASSERT(base_storage != nullptr);
|
||||
|
||||
if (m_reader->GetStorage() && m_reader->GetStorage()->HasDecryptedSections()) {
|
||||
*out = std::move(base_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// Create the iv.
|
||||
std::array<u8, AesXtsStorage::IvSize> iv{};
|
||||
AesXtsStorage::MakeAesXtsIv(iv.data(), sizeof(iv), offset, NcaHeader::XtsBlockSize);
|
||||
|
|
@ -809,6 +830,12 @@ Result NcaFileSystemDriver::CreateAesCtrExStorageMetaStorage(
|
|||
// Validate preconditions.
|
||||
ASSERT(out != nullptr);
|
||||
ASSERT(base_storage != nullptr);
|
||||
|
||||
if (m_reader->GetStorage() && m_reader->GetStorage()->HasDecryptedSections()) {
|
||||
*out = std::move(base_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
ASSERT(encryption_type == NcaFsHeader::EncryptionType::None ||
|
||||
encryption_type == NcaFsHeader::EncryptionType::AesCtrEx ||
|
||||
encryption_type == NcaFsHeader::EncryptionType::AesCtrExSkipLayerHash);
|
||||
|
|
@ -871,6 +898,11 @@ Result NcaFileSystemDriver::CreateAesCtrExStorage(
|
|||
ASSERT(meta_storage != nullptr);
|
||||
ASSERT(patch_info.HasAesCtrExTable());
|
||||
|
||||
if (m_reader->GetStorage() && m_reader->GetStorage()->HasDecryptedSections()) {
|
||||
*out = std::move(base_storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// Read the bucket tree header.
|
||||
BucketTree::Header header;
|
||||
std::memcpy(std::addressof(header), patch_info.aes_ctr_ex_header.data(), sizeof(header));
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ public:
|
|||
const NcaCompressionConfiguration& compression_cfg);
|
||||
|
||||
VirtualFile GetSharedBodyStorage();
|
||||
VirtualFile GetStorage() const { return m_body_storage; }
|
||||
u32 GetMagic() const;
|
||||
NcaHeader::DistributionType GetDistributionType() const;
|
||||
NcaHeader::ContentType GetContentType() const;
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ public:
|
|||
// Returns whether or not the file can be read from.
|
||||
virtual bool IsReadable() const = 0;
|
||||
|
||||
// Returns true if this file serves NCA body data that is already decrypted (e.g. NCZ).
|
||||
virtual bool HasDecryptedSections() const { return false; }
|
||||
|
||||
// The primary method of reading from the file. Reads length bytes into data starting at offset
|
||||
// into file. Returns number of bytes successfully read.
|
||||
virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ public:
|
|||
|
||||
std::size_t GetOffset() const;
|
||||
|
||||
bool HasDecryptedSections() const override {
|
||||
return file && file->HasDecryptedSections();
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue