[fs] temporarely disable nca verification (#298)

This adds a passthrough to basically disable nca verification for newer NCAs, this fixes (tested) Pokemon 4.0.0 update and other newer SDK games and updates (as reported on the discord)

This is implemented as toggle that is default enabled, this needs proper implementation in the future.

Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/298
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
Maufeat 2025-09-05 00:04:37 +02:00 committed by crueter
parent bbcd8aded6
commit 718891d11f
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
61 changed files with 559 additions and 129 deletions

View file

@ -88,6 +88,8 @@ add_library(core STATIC
file_sys/fssystem/fssystem_crypto_configuration.h
file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp
file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h
file_sys/fssystem/fssystem_hierarchical_sha3_storage.cpp
file_sys/fssystem/fssystem_hierarchical_sha3_storage.h
file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
file_sys/fssystem/fssystem_hierarchical_sha256_storage.h
file_sys/fssystem/fssystem_indirect_storage.cpp
@ -102,6 +104,7 @@ add_library(core STATIC
file_sys/fssystem/fssystem_nca_header.cpp
file_sys/fssystem/fssystem_nca_header.h
file_sys/fssystem/fssystem_nca_reader.cpp
file_sys/fssystem/fssystem_passthrough_storage.h
file_sys/fssystem/fssystem_pooled_buffer.cpp
file_sys/fssystem/fssystem_pooled_buffer.h
file_sys/fssystem/fssystem_sparse_storage.cpp

View file

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
#include "core/file_sys/fssystem/fssystem_bucket_tree_utils.h"
@ -233,7 +237,10 @@ Result BucketTree::Initialize(VirtualFile node_storage, VirtualFile entry_storag
void BucketTree::Initialize(size_t node_size, s64 end_offset) {
ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax);
ASSERT(Common::IsPowerOfTwo(node_size));
ASSERT(end_offset > 0);
if (!Settings::values.disable_nca_verification.GetValue()) {
ASSERT(end_offset > 0);
}
ASSERT(!this->IsInitialized());
m_node_size = node_size;

View file

@ -5,23 +5,10 @@
#include "common/scope_exit.h"
#include "core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h"
#include <cmath>
namespace FileSys {
namespace {
s32 Log2(s32 value) {
ASSERT(value > 0);
ASSERT(Common::IsPowerOfTwo(value));
s32 log = 0;
while ((value >>= 1) > 0) {
++log;
}
return log;
}
} // namespace
Result HierarchicalSha256Storage::Initialize(VirtualFile* base_storages, s32 layer_count,
size_t htbs, void* hash_buf, size_t hash_buf_size) {
// Validate preconditions.
@ -31,7 +18,8 @@ Result HierarchicalSha256Storage::Initialize(VirtualFile* base_storages, s32 lay
// Set size tracking members.
m_hash_target_block_size = static_cast<s32>(htbs);
m_log_size_ratio = Log2(m_hash_target_block_size / HashSize);
m_log_size_ratio =
static_cast<s32>(std::log2(static_cast<double>(m_hash_target_block_size) / HashSize));
// Get the base storage size.
m_base_storage_size = base_storages[2]->GetSize();

View file

@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/alignment.h"
#include "common/scope_exit.h"
#include "core/file_sys/fssystem/fssystem_hierarchical_sha3_storage.h"
#include <cmath>
namespace FileSys {
Result HierarchicalSha3Storage::Initialize(VirtualFile* base_storages, s32 layer_count, size_t htbs,
void* hash_buf, size_t hash_buf_size) {
ASSERT(layer_count == LayerCount);
ASSERT(Common::IsPowerOfTwo(htbs));
ASSERT(hash_buf != nullptr);
m_hash_target_block_size = static_cast<s32>(htbs);
m_log_size_ratio =
static_cast<s32>(std::log2(static_cast<double>(m_hash_target_block_size) / HashSize));
m_base_storage_size = base_storages[2]->GetSize();
{
auto size_guard = SCOPE_GUARD {
m_base_storage_size = 0;
};
R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize)
<< m_log_size_ratio << m_log_size_ratio,
ResultHierarchicalSha256BaseStorageTooLarge);
size_guard.Cancel();
}
m_base_storage = base_storages[2];
m_hash_buffer = static_cast<char*>(hash_buf);
m_hash_buffer_size = hash_buf_size;
std::array<u8, HashSize> master_hash{};
base_storages[0]->ReadObject(std::addressof(master_hash));
s64 hash_storage_size = base_storages[1]->GetSize();
ASSERT(Common::IsAligned(hash_storage_size, HashSize));
ASSERT(hash_storage_size <= m_hash_target_block_size);
ASSERT(hash_storage_size <= static_cast<s64>(m_hash_buffer_size));
base_storages[1]->Read(reinterpret_cast<u8*>(m_hash_buffer),
static_cast<size_t>(hash_storage_size), 0);
R_SUCCEED();
}
size_t HierarchicalSha3Storage::Read(u8* buffer, size_t size, size_t offset) const {
if (size == 0)
return size;
ASSERT(buffer != nullptr);
return m_base_storage->Read(buffer, size, offset);
}
} // namespace FileSys

View file

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <mutex>
#include "core/file_sys/errors.h"
#include "core/file_sys/fssystem/fs_i_storage.h"
#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
class HierarchicalSha3Storage : public IReadOnlyStorage {
YUZU_NON_COPYABLE(HierarchicalSha3Storage);
YUZU_NON_MOVEABLE(HierarchicalSha3Storage);
public:
static constexpr s32 LayerCount = 3;
static constexpr size_t HashSize = 256 / 8; // SHA3-256
public:
HierarchicalSha3Storage() : m_mutex() {}
Result Initialize(VirtualFile* base_storages, s32 layer_count, size_t htbs, void* hash_buf,
size_t hash_buf_size);
virtual size_t GetSize() const override {
return m_base_storage->GetSize();
}
virtual size_t Read(u8* buffer, size_t length, size_t offset) const override;
private:
VirtualFile m_base_storage;
s64 m_base_storage_size{};
char* m_hash_buffer{};
size_t m_hash_buffer_size{};
s32 m_hash_target_block_size{};
s32 m_log_size_ratio{};
std::mutex m_mutex;
};
} // namespace FileSys

View file

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h"
#include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h"
#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h"
@ -10,10 +14,12 @@
#include "core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h"
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
#include "core/file_sys/fssystem/fssystem_integrity_romfs_storage.h"
#include "core/file_sys/fssystem/fssystem_passthrough_storage.h"
#include "core/file_sys/fssystem/fssystem_memory_resource_buffer_hold_storage.h"
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
#include "core/file_sys/fssystem/fssystem_sparse_storage.h"
#include "core/file_sys/fssystem/fssystem_switch_storage.h"
#include "core/file_sys/fssystem/fssystem_hierarchical_sha3_storage.h"
#include "core/file_sys/vfs/vfs_offset.h"
#include "core/file_sys/vfs/vfs_vector.h"
@ -299,18 +305,24 @@ Result NcaFileSystemDriver::CreateStorageByRawStorage(VirtualFile* out,
// Process hash/integrity layer.
switch (header_reader->GetHashType()) {
case NcaFsHeader::HashType::HierarchicalSha256Hash:
R_TRY(this->CreateSha256Storage(std::addressof(storage), std::move(storage),
header_reader->GetHashData().hierarchical_sha256_data));
R_TRY(CreateSha256Storage(&storage, std::move(storage),
header_reader->GetHashData().hierarchical_sha256_data));
break;
case NcaFsHeader::HashType::HierarchicalIntegrityHash:
R_TRY(this->CreateIntegrityVerificationStorage(
std::addressof(storage), std::move(storage),
header_reader->GetHashData().integrity_meta_info));
R_TRY(CreateIntegrityVerificationStorage(&storage, std::move(storage),
header_reader->GetHashData().integrity_meta_info));
break;
case NcaFsHeader::HashType::HierarchicalSha3256Hash:
R_TRY(CreateSha3Storage(&storage, std::move(storage),
header_reader->GetHashData().hierarchical_sha256_data));
break;
default:
LOG_ERROR(Loader, "Unhandled Fs HashType enum={}",
static_cast<int>(header_reader->GetHashType()));
R_THROW(ResultInvalidNcaFsHeaderHashType);
}
// Process compression layer.
if (header_reader->ExistsCompressionLayer()) {
R_TRY(this->CreateCompressedStorage(
@ -679,6 +691,7 @@ Result NcaFileSystemDriver::CreateSparseStorageMetaStorageWithVerification(
// Create the verification storage.
VirtualFile integrity_storage;
Result rc = this->CreateIntegrityVerificationStorageForMeta(
std::addressof(integrity_storage), out_layer_info_storage, std::move(decrypted_storage),
meta_offset, meta_data_hash_data_info);
@ -734,8 +747,26 @@ Result NcaFileSystemDriver::CreateSparseStorageWithVerification(
NcaHeader::CtrBlockSize)));
// Check the meta data hash type.
R_UNLESS(meta_data_hash_type == NcaFsHeader::MetaDataHashType::HierarchicalIntegrity,
ResultRomNcaInvalidSparseMetaDataHashType);
if (meta_data_hash_type != NcaFsHeader::MetaDataHashType::HierarchicalIntegrity) {
LOG_ERROR(Loader, "Sparse meta hash type {} not supported for verification; mounting sparse data WITHOUT verification (temporary).", static_cast<int>(meta_data_hash_type));
R_TRY(this->CreateBodySubStorage(std::addressof(body_substorage),
sparse_info.physical_offset,
sparse_info.GetPhysicalSize()));
// Create sparse core directly (no meta verification)
std::shared_ptr<SparseStorage> sparse_storage_fallback;
R_TRY(this->CreateSparseStorageCore(std::addressof(sparse_storage_fallback),
body_substorage, sparse_info.GetPhysicalSize(),
/*meta_storage*/ body_substorage, // dummy; not used
sparse_info, false));
if (out_sparse_storage)
*out_sparse_storage = sparse_storage_fallback;
*out_fs_data_offset = fs_offset;
*out = std::move(sparse_storage_fallback);
R_SUCCEED();
}
// Create the meta storage.
VirtualFile meta_storage;
@ -1093,6 +1124,56 @@ Result NcaFileSystemDriver::CreatePatchMetaStorage(
R_SUCCEED();
}
Result NcaFileSystemDriver::CreateSha3Storage(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& hash_data) {
ASSERT(out != nullptr);
ASSERT(base_storage != nullptr);
using VerificationStorage = HierarchicalSha3Storage;
R_UNLESS(Common::IsPowerOfTwo(hash_data.hash_block_size),
ResultInvalidHierarchicalSha256BlockSize);
R_UNLESS(hash_data.hash_layer_count == VerificationStorage::LayerCount - 1,
ResultInvalidHierarchicalSha256LayerCount);
const auto& hash_region = hash_data.hash_layer_region[0];
const auto& data_region = hash_data.hash_layer_region[1];
constexpr s32 CacheBlockCount = 2;
const auto hash_buffer_size = static_cast<size_t>(hash_region.size);
const auto cache_buffer_size = CacheBlockCount * hash_data.hash_block_size;
const auto total_buffer_size = hash_buffer_size + cache_buffer_size;
auto buffer_hold_storage = std::make_shared<MemoryResourceBufferHoldStorage>(
std::move(base_storage), total_buffer_size);
R_UNLESS(buffer_hold_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
R_UNLESS(buffer_hold_storage->IsValid(), ResultAllocationMemoryFailedInNcaFileSystemDriverI);
s64 base_size = buffer_hold_storage->GetSize();
R_UNLESS(hash_region.offset + hash_region.size <= base_size, ResultNcaBaseStorageOutOfRangeC);
R_UNLESS(data_region.offset + data_region.size <= base_size, ResultNcaBaseStorageOutOfRangeC);
auto master_hash_storage =
std::make_shared<ArrayVfsFile<sizeof(Hash)>>(hash_data.fs_data_master_hash.value);
auto verification_storage = std::make_shared<VerificationStorage>();
R_UNLESS(verification_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
std::array<VirtualFile, VerificationStorage::LayerCount> layer_storages{
std::make_shared<OffsetVfsFile>(master_hash_storage, sizeof(Hash), 0),
std::make_shared<OffsetVfsFile>(buffer_hold_storage, hash_region.size, hash_region.offset),
std::make_shared<OffsetVfsFile>(buffer_hold_storage, data_region.size, data_region.offset),
};
R_TRY(verification_storage->Initialize(layer_storages.data(), VerificationStorage::LayerCount,
hash_data.hash_block_size,
buffer_hold_storage->GetBuffer(), hash_buffer_size));
*out = std::move(verification_storage);
R_SUCCEED();
}
Result NcaFileSystemDriver::CreateSha256Storage(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& hash_data) {
@ -1160,6 +1241,7 @@ Result NcaFileSystemDriver::CreateSha256Storage(
Result NcaFileSystemDriver::CreateIntegrityVerificationStorage(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::IntegrityMetaInfo& meta_info) {
R_RETURN(this->CreateIntegrityVerificationStorageImpl(
out, base_storage, meta_info, 0, IntegrityDataCacheCount, IntegrityHashCacheCount,
HierarchicalIntegrityVerificationStorage::GetDefaultDataCacheBufferLevel(
@ -1209,63 +1291,96 @@ Result NcaFileSystemDriver::CreateIntegrityVerificationStorageImpl(
VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::IntegrityMetaInfo& meta_info, s64 layer_info_offset,
int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level) {
// Validate preconditions.
// Preconditions
ASSERT(out != nullptr);
ASSERT(base_storage != nullptr);
ASSERT(layer_info_offset >= 0);
// Define storage types.
using VerificationStorage = HierarchicalIntegrityVerificationStorage;
using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
if (!Settings::values.disable_nca_verification.GetValue()) {
// Define storage types.
using VerificationStorage = HierarchicalIntegrityVerificationStorage;
using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
// Validate the meta info.
HierarchicalIntegrityVerificationInformation level_hash_info;
std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info),
sizeof(level_hash_info));
// Validate the meta info.
HierarchicalIntegrityVerificationInformation level_hash_info;
std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info),
sizeof(level_hash_info));
R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
// Get the base storage size.
s64 base_storage_size = base_storage->GetSize();
// Get the base storage size.
s64 base_storage_size = base_storage->GetSize();
// Create storage info.
StorageInfo storage_info;
for (s32 i = 0; i < static_cast<s32>(level_hash_info.max_layers - 2); ++i) {
const auto& layer_info = level_hash_info.info[i];
R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size,
// Create storage info.
StorageInfo storage_info;
for (s32 i = 0; i < static_cast<s32>(level_hash_info.max_layers - 2); ++i) {
const auto& layer_info = level_hash_info.info[i];
R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
storage_info[i + 1] = std::make_shared<OffsetVfsFile>(
base_storage, layer_info.size, layer_info_offset + layer_info.offset);
}
// Set the last layer info.
const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get();
R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
if (layer_info_offset > 0) {
R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset,
ResultRomNcaInvalidIntegrityLayerInfoOffset);
}
storage_info.SetDataStorage(std::make_shared<OffsetVfsFile>(
std::move(base_storage), layer_info.size, last_layer_info_offset));
storage_info[i + 1] = std::make_shared<OffsetVfsFile>(
base_storage, layer_info.size, layer_info_offset + layer_info.offset);
// Make the integrity romfs storage.
auto integrity_storage = std::make_shared<IntegrityRomFsStorage>();
R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
// Initialize the integrity storage.
R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info,
max_data_cache_entries, max_hash_cache_entries,
buffer_level));
// Set the output.
*out = std::move(integrity_storage);
R_SUCCEED();
} else {
// Read IVFC layout
HierarchicalIntegrityVerificationInformation lhi{};
std::memcpy(std::addressof(lhi), std::addressof(meta_info.level_hash_info), sizeof(lhi));
R_UNLESS(IntegrityMinLayerCount <= lhi.max_layers,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
R_UNLESS(lhi.max_layers <= IntegrityMaxLayerCount,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
const auto& data_li = lhi.info[lhi.max_layers - 2];
const s64 base_size = base_storage->GetSize();
// Compute the data layer window
const s64 data_off = (layer_info_offset > 0) ? 0LL : data_li.offset.Get();
R_UNLESS(data_off + data_li.size <= base_size, ResultNcaBaseStorageOutOfRangeD);
if (layer_info_offset > 0) {
R_UNLESS(data_off + data_li.size <= layer_info_offset,
ResultRomNcaInvalidIntegrityLayerInfoOffset);
}
// TODO: Passthrough (temporary compatibility: integrity disabled)
auto data_view = std::make_shared<OffsetVfsFile>(base_storage, data_li.size, data_off);
R_UNLESS(data_view != nullptr, ResultAllocationMemoryFailedAllocateShared);
auto passthrough = std::make_shared<PassthroughStorage>(std::move(data_view));
R_UNLESS(passthrough != nullptr, ResultAllocationMemoryFailedAllocateShared);
*out = std::move(passthrough);
R_SUCCEED();
}
// Set the last layer info.
const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get();
R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
if (layer_info_offset > 0) {
R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset,
ResultRomNcaInvalidIntegrityLayerInfoOffset);
}
storage_info.SetDataStorage(std::make_shared<OffsetVfsFile>(
std::move(base_storage), layer_info.size, last_layer_info_offset));
// Make the integrity romfs storage.
auto integrity_storage = std::make_shared<IntegrityRomFsStorage>();
R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
// Initialize the integrity storage.
R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info,
max_data_cache_entries, max_hash_cache_entries,
buffer_level));
// Set the output.
*out = std::move(integrity_storage);
R_SUCCEED();
}
Result NcaFileSystemDriver::CreateRegionSwitchStorage(VirtualFile* out,

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -329,6 +332,10 @@ private:
const NcaPatchInfo& patch_info,
const NcaMetaDataHashDataInfo& meta_data_hash_data_info);
Result CreateSha3Storage(VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& hash_data);
Result CreateSha256Storage(VirtualFile* out, VirtualFile base_storage,
const NcaFsHeader::HashData::HierarchicalSha256Data& sha256_data);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -10,11 +13,13 @@ u8 NcaHeader::GetProperKeyGeneration() const {
}
bool NcaPatchInfo::HasIndirectTable() const {
return this->indirect_size != 0;
static constexpr unsigned char BKTR[4] = {'B', 'K', 'T', 'R'};
return std::memcmp(indirect_header.data(), BKTR, sizeof(BKTR)) == 0;
}
bool NcaPatchInfo::HasAesCtrExTable() const {
return this->aes_ctr_ex_size != 0;
static constexpr unsigned char BKTR[4] = {'B', 'K', 'T', 'R'};
return std::memcmp(aes_ctr_ex_header.data(), BKTR, sizeof(BKTR)) == 0;
}
} // namespace FileSys

View file

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/file_sys/fssystem/fs_i_storage.h"
#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
//TODO: No integrity verification.
class PassthroughStorage final : public IReadOnlyStorage {
YUZU_NON_COPYABLE(PassthroughStorage);
YUZU_NON_MOVEABLE(PassthroughStorage);
public:
explicit PassthroughStorage(VirtualFile base) : base_(std::move(base)) {}
~PassthroughStorage() override = default;
size_t Read(u8* buffer, size_t size, size_t offset) const override {
if (!base_ || size == 0)
return 0;
return base_->Read(buffer, size, offset);
}
size_t GetSize() const override {
return base_ ? base_->GetSize() : 0;
}
private:
VirtualFile base_{};
};
} // namespace FileSys

View file

@ -1,5 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator
// Project// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/am/applet.h"
@ -12,7 +15,7 @@ Applet::Applet(Core::System& system, std::unique_ptr<Process> process_, bool is_
process(std::move(process_)), hid_registration(system, *process),
gpu_error_detected_event(context), friend_invitation_storage_channel_event(context),
notification_storage_channel_event(context), health_warning_disappeared_system_event(context),
acquired_sleep_lock_event(context), pop_from_general_channel_event(context),
unknown_event(context), acquired_sleep_lock_event(context), pop_from_general_channel_event(context),
library_applet_launchable_event(context), accumulated_suspended_tick_changed_event(context),
sleep_lock_event(context), state_changed_event(context) {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -120,6 +123,7 @@ struct Applet {
Event friend_invitation_storage_channel_event;
Event notification_storage_channel_event;
Event health_warning_disappeared_system_event;
Event unknown_event;
Event acquired_sleep_lock_event;
Event pop_from_general_channel_event;
Event library_applet_launchable_event;

View file

@ -18,6 +18,7 @@ IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& sys
// clang-format off
static const FunctionInfo functions[] = {
{100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"},
{110, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxyEx"},
{200, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld>, "OpenLibraryAppletProxyOld"},
{201, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxy>, "OpenLibraryAppletProxy"},
{300, nullptr, "OpenOverlayAppletProxy"},
@ -25,6 +26,7 @@ IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& sys
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
{410, nullptr, "GetSystemAppletControllerForDebug"},
{450, D<&IAllSystemAppletProxiesService::GetSystemProcessCommonFunctions>, "GetSystemProcessCommonFunctions"}, // 19.0.0+
{460, D<&IAllSystemAppletProxiesService::GetAppletAlternativeFunctions>, "GetAppletAlternativeFunctions"}, // 20.0.0+
{1000, nullptr, "GetDebugFunctions"},
};
// clang-format on
@ -99,6 +101,14 @@ Result IAllSystemAppletProxiesService::GetSystemProcessCommonFunctions() {
R_SUCCEED();
}
Result IAllSystemAppletProxiesService::GetAppletAlternativeFunctions() {
LOG_DEBUG(Service_AM, "(STUBBED) called.");
// TODO (maufeat)
R_SUCCEED();
}
std::shared_ptr<Applet> IAllSystemAppletProxiesService::GetAppletFromProcessId(
ProcessId process_id) {
return m_window_system.GetByAppletResourceUserId(process_id.pid);

View file

@ -39,6 +39,7 @@ private:
InCopyHandle<Kernel::KProcess> process_handle,
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
Result GetSystemProcessCommonFunctions();
Result GetAppletAlternativeFunctions();
private:
std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);

View file

@ -35,6 +35,7 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
{310, nullptr, "IsSystemAppletHomeMenu"}, //19.0.0+
{320, nullptr, "SetGpuTimeSliceBoost"}, //19.0.0+
{321, nullptr, "SetGpuTimeSliceBoostDueToApplication"}, //19.0.0+
{350, D<&IAppletCommonFunctions::Unknown350>, "Unknown350"} //20.0.0+
};
// clang-format on
@ -70,4 +71,10 @@ Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_
R_SUCCEED();
}
Result IAppletCommonFunctions::Unknown350(Out<u16> out_unknown) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_unknown = 0;
R_SUCCEED();
}
} // namespace Service::AM

View file

@ -20,6 +20,7 @@ private:
Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled);
Result SetCpuBoostRequestPriority(s32 priority);
Result GetCurrentApplicationId(Out<u64> out_application_id);
Result Unknown350(Out<u16> out_unknown);
const std::shared_ptr<Applet> applet;
};

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -85,6 +88,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
{181, nullptr, "UpgradeLaunchRequiredVersion"},
{190, nullptr, "SendServerMaintenanceOverlayNotification"},
{200, nullptr, "GetLastApplicationExitReason"},
{210, D<&IApplicationFunctions::GetUnknownEvent210>, "Unknown210"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"},
@ -487,6 +491,13 @@ Result IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(
R_SUCCEED();
}
Result IApplicationFunctions::GetUnknownEvent210(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_AM, "called");
*out_event = m_applet->unknown_event.GetHandle();
R_SUCCEED();
}
Result IApplicationFunctions::PrepareForJit() {
LOG_WARNING(Service_AM, "(STUBBED) called");

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -76,6 +79,7 @@ private:
Result TryPopFromFriendInvitationStorageChannel(Out<SharedPointer<IStorage>> out_storage);
Result GetNotificationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetUnknownEvent210(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result PrepareForJit();
const std::shared_ptr<Applet> m_applet;

View file

@ -113,9 +113,11 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
Firmware1700 = 17,
Firmware1800 = 18,
Firmware1900 = 19,
Firmware2000 = 20,
Firmware2100 = 21,
};
auto process = CreateProcess(system, program_id, Firmware1400, Firmware1900);
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2100);
if (!process) {
// Couldn't initialize the guest process
return {};

View file

@ -306,6 +306,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{3013, nullptr, "IsGameCardEnabled"},
{3014, nullptr, "IsLocalContentShareEnabled"},
{3050, nullptr, "ListAssignELicenseTaskResult"},
{4022, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4022"},
{4023, D<&IApplicationManagerInterface::Unknown4023>, "Unknown4023"},
{4088, D<&IApplicationManagerInterface::Unknown4022>, "Unknown4088"},
{9999, nullptr, "GetApplicationCertificate"},
};
// clang-format on
@ -523,4 +526,17 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> o
R_SUCCEED();
}
Result IApplicationManagerInterface::Unknown4022(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_NS, "(STUBBED) called");
*out_event = gamecard_update_detection_event.GetHandle();
R_SUCCEED();
}
Result IApplicationManagerInterface::Unknown4023(Out<u64> out_result) {
LOG_WARNING(Service_NS, "(STUBBED) called.");
*out_result = 0;
R_SUCCEED();
}
} // namespace Service::NS

View file

@ -53,6 +53,8 @@ public:
u64 application_id);
Result CheckApplicationLaunchVersion(u64 application_id);
Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result Unknown4023(Out<u64> out_result);
private:
KernelHelpers::ServiceContext service_context;

View file

@ -80,11 +80,12 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili
{1451, D<&IParentalControlService::StartPlayTimer>, "StartPlayTimer"},
{1452, D<&IParentalControlService::StopPlayTimer>, "StopPlayTimer"},
{1453, D<&IParentalControlService::IsPlayTimerEnabled>, "IsPlayTimerEnabled"},
{1454, nullptr, "GetPlayTimerRemainingTime"},
{1454, D<&IParentalControlService::GetPlayTimerRemainingTime>, "GetPlayTimerRemainingTime"},
{1455, D<&IParentalControlService::IsRestrictedByPlayTimer>, "IsRestrictedByPlayTimer"},
{1456, D<&IParentalControlService::GetPlayTimerSettingsOld>, "GetPlayTimerSettingsOld"},
{1457, D<&IParentalControlService::GetPlayTimerEventToRequestSuspension>, "GetPlayTimerEventToRequestSuspension"},
{1458, D<&IParentalControlService::IsPlayTimerAlarmDisabled>, "IsPlayTimerAlarmDisabled"},
{1459, D<&IParentalControlService::GetPlayTimerRemainingTimeDisplayInfo>, "GetPlayTimerRemainingTimeDisplayInfo"},
{1471, nullptr, "NotifyWrongPinCodeInputManyTimes"},
{1472, nullptr, "CancelNetworkRequest"},
{1473, D<&IParentalControlService::GetUnlinkedEvent>, "GetUnlinkedEvent"},
@ -378,6 +379,12 @@ Result IParentalControlService::IsPlayTimerEnabled(Out<bool> out_is_play_timer_e
R_SUCCEED();
}
Result IParentalControlService::GetPlayTimerRemainingTime(Out<s32> out_remaining_time) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
*out_remaining_time = std::numeric_limits<s32>::max();
R_SUCCEED();
}
Result IParentalControlService::IsRestrictedByPlayTimer(Out<bool> out_is_restricted_by_play_timer) {
*out_is_restricted_by_play_timer = false;
LOG_WARNING(Service_PCTL, "(STUBBED) called, restricted={}", *out_is_restricted_by_play_timer);
@ -412,6 +419,11 @@ Result IParentalControlService::IsPlayTimerAlarmDisabled(Out<bool> out_play_time
R_SUCCEED();
}
Result IParentalControlService::GetPlayTimerRemainingTimeDisplayInfo(/* Out 0x18 */) {
LOG_INFO(Service_PCTL, "called");
R_SUCCEED();
}
Result IParentalControlService::GetUnlinkedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_INFO(Service_PCTL, "called");
*out_event = unlinked_event.GetHandle();

View file

@ -49,10 +49,12 @@ private:
Result StartPlayTimer();
Result StopPlayTimer();
Result IsPlayTimerEnabled(Out<bool> out_is_play_timer_enabled);
Result GetPlayTimerRemainingTime(Out<s32> out_remaining_time);
Result IsRestrictedByPlayTimer(Out<bool> out_is_restricted_by_play_timer);
Result GetPlayTimerSettingsOld(Out<PlayTimerSettings> out_play_timer_settings);
Result GetPlayTimerEventToRequestSuspension(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result IsPlayTimerAlarmDisabled(Out<bool> out_play_timer_alarm_disabled);
Result GetPlayTimerRemainingTimeDisplayInfo();
Result GetUnlinkedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetStereoVisionRestriction(Out<bool> out_stereo_vision_restriction);
Result SetStereoVisionRestriction(bool stereo_vision_restriction);