mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-27 17:58:59 +02:00
[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:
parent
bbcd8aded6
commit
718891d11f
61 changed files with 559 additions and 129 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
32
src/core/file_sys/fssystem/fssystem_passthrough_storage.h
Normal file
32
src/core/file_sys/fssystem/fssystem_passthrough_storage.h
Normal 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
|
||||
|
|
@ -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) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue