fix: add missing NCA encryption type support and patch handling

Changes:
- Add missing encryption type cases to all switch statements
- Only call CreatePatchMetaStorage() when both tables are present
- Update assertions to allow partial table combinations

Fixes loading for Hades II, Pokémon Legends: Z-A, and other games
with newer NCA structures.

Reported-by: Dr.Stug@citron-emu.org
Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-10-24 16:25:23 +10:00
parent 7672d2f1ff
commit 20250c3e31

View File

@@ -140,7 +140,7 @@ Result NcaFileSystemDriver::OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader*
const auto& patch_info = out_header_reader->GetPatchInfo();
VirtualFile patch_meta_aes_ctr_ex_meta_storage;
VirtualFile patch_meta_indirect_meta_storage;
if (out_header_reader->ExistsPatchMetaHashLayer()) {
if (out_header_reader->ExistsPatchMetaHashLayer() && patch_info.HasAesCtrExTable()) {
// Check the meta hash type.
R_UNLESS(out_header_reader->GetPatchMetaHashType() ==
NcaFsHeader::MetaDataHashType::HierarchicalIntegrity,
@@ -165,8 +165,8 @@ Result NcaFileSystemDriver::OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader*
// Create the ex meta storage.
VirtualFile aes_ctr_ex_storage_meta_storage = patch_meta_aes_ctr_ex_meta_storage;
if (aes_ctr_ex_storage_meta_storage == nullptr) {
// If we don't have a meta storage, we must not have a patch meta hash layer.
ASSERT(!out_header_reader->ExistsPatchMetaHashLayer());
// If we don't have a meta storage, we must not have a patch meta hash layer with both tables.
ASSERT(!out_header_reader->ExistsPatchMetaHashLayer() || !patch_info.HasIndirectTable());
R_TRY(this->CreateAesCtrExStorageMetaStorage(
std::addressof(aes_ctr_ex_storage_meta_storage), storage, fs_data_offset,
@@ -202,11 +202,13 @@ Result NcaFileSystemDriver::OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader*
fs_data_offset));
break;
case NcaFsHeader::EncryptionType::AesCtr:
case NcaFsHeader::EncryptionType::AesCtrEx:
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage),
fs_data_offset, out_header_reader->GetAesCtrUpperIv(),
AlignmentStorageRequirement::None));
break;
case NcaFsHeader::EncryptionType::AesCtrSkipLayerHash: {
case NcaFsHeader::EncryptionType::AesCtrSkipLayerHash:
case NcaFsHeader::EncryptionType::AesCtrExSkipLayerHash: {
// Create the aes ctr storage.
VirtualFile aes_ctr_storage;
R_TRY(this->CreateAesCtrStorage(std::addressof(aes_ctr_storage), storage,
@@ -232,8 +234,8 @@ Result NcaFileSystemDriver::OpenStorageImpl(VirtualFile* out, NcaFsHeaderReader*
// Create the indirect meta storage.
VirtualFile indirect_storage_meta_storage = patch_meta_indirect_meta_storage;
if (indirect_storage_meta_storage == nullptr) {
// If we don't have a meta storage, we must not have a patch meta hash layer.
ASSERT(!out_header_reader->ExistsPatchMetaHashLayer());
// If we don't have a meta storage, we must not have a patch meta hash layer with AesCtrEx.
ASSERT(!out_header_reader->ExistsPatchMetaHashLayer() || !patch_info.HasAesCtrExTable());
R_TRY(this->CreateIndirectStorageMetaStorage(
std::addressof(indirect_storage_meta_storage), storage, patch_info));
@@ -382,10 +384,33 @@ Result NcaFileSystemDriver::OpenIndirectableStorageAsOriginal(
this->CreateAesXtsStorage(std::addressof(storage), std::move(storage), fs_data_offset));
break;
case NcaFsHeader::EncryptionType::AesCtr:
case NcaFsHeader::EncryptionType::AesCtrEx:
R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage), fs_data_offset,
header_reader->GetAesCtrUpperIv(),
AlignmentStorageRequirement::CacheBlockSize));
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,
header_reader->GetAesCtrUpperIv(),
AlignmentStorageRequirement::CacheBlockSize));
// Create region switch storage.
R_TRY(this->CreateRegionSwitchStorage(std::addressof(storage), header_reader,
std::move(storage), std::move(aes_ctr_storage)));
} break;
case NcaFsHeader::EncryptionType::AesCtrExSkipLayerHash: {
// Create the aes ctr ex storage.
VirtualFile aes_ctr_ex_storage;
R_TRY(this->CreateAesCtrStorage(std::addressof(aes_ctr_ex_storage), storage, fs_data_offset,
header_reader->GetAesCtrUpperIv(),
AlignmentStorageRequirement::CacheBlockSize));
// Create region switch storage.
R_TRY(this->CreateRegionSwitchStorage(std::addressof(storage), header_reader,
std::move(storage), std::move(aes_ctr_ex_storage)));
} break;
default:
R_THROW(ResultInvalidNcaFsHeaderEncryptionType);
}