summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-x.travis/clang-format/script.sh2
m---------externals/mbedtls0
-rw-r--r--src/common/web_result.h1
-rw-r--r--src/core/CMakeLists.txt7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp43
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/core_cpu.cpp14
-rw-r--r--src/core/core_cpu.h17
-rw-r--r--src/core/crypto/key_manager.cpp826
-rw-r--r--src/core/crypto/key_manager.h104
-rw-r--r--src/core/crypto/partition_data_manager.cpp593
-rw-r--r--src/core/crypto/partition_data_manager.h109
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h8
-rw-r--r--src/core/file_sys/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp14
-rw-r--r--src/core/file_sys/registered_cache.h12
-rw-r--r--src/core/file_sys/sdmc_factory.cpp8
-rw-r--r--src/core/file_sys/sdmc_factory.h4
-rw-r--r--src/core/file_sys/vfs.h10
-rw-r--r--src/core/file_sys/vfs_types.h21
-rw-r--r--src/core/gdbstub/gdbstub.cpp6
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp34
-rw-r--r--src/core/hle/kernel/process.h49
-rw-r--r--src/core/hle/kernel/svc.cpp39
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/kernel/thread.cpp16
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp29
-rw-r--r--src/core/hle/service/filesystem/filesystem.h12
-rw-r--r--src/core/hle/service/ns/pl_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/hle/service/vi/vi.cpp50
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/elf.cpp32
-rw-r--r--src/core/loader/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp29
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp42
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/video_core/engines/fermi_2d.cpp14
-rw-r--r--src/video_core/engines/maxwell_dma.cpp10
-rw-r--r--src/video_core/engines/maxwell_dma.h4
-rw-r--r--src/video_core/engines/shader_bytecode.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp59
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h20
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp8
-rw-r--r--src/video_core/textures/decoders.cpp209
-rw-r--r--src/video_core/textures/decoders.h21
-rw-r--r--src/video_core/textures/texture.h1
-rw-r--r--src/web_service/telemetry_json.cpp90
-rw-r--r--src/web_service/telemetry_json.h24
-rw-r--r--src/web_service/verify_login.cpp1
-rw-r--r--src/web_service/web_backend.cpp235
-rw-r--r--src/web_service/web_backend.h58
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp5
-rw-r--r--src/yuzu/debugger/wait_tree.cpp9
-rw-r--r--src/yuzu/game_list_worker.cpp19
-rw-r--r--src/yuzu/game_list_worker.h2
-rw-r--r--src/yuzu/main.cpp118
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
73 files changed, 2509 insertions, 668 deletions
diff --git a/.travis/clang-format/script.sh b/.travis/clang-format/script.sh
index 0f6e8e6e4..3eff6322f 100755
--- a/.travis/clang-format/script.sh
+++ b/.travis/clang-format/script.sh
@@ -1,6 +1,6 @@
#!/bin/bash -ex
-if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \
+if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \
dist/*.svg dist/*.xml; then
echo Trailing whitespace found, aborting
exit 1
diff --git a/externals/mbedtls b/externals/mbedtls
-Subproject d409b75a4cf75a5b358b352c75826ddbca44db5
+Subproject a280e602f3a4ae001d3a83cbc3e6e04c99c2227
diff --git a/src/common/web_result.h b/src/common/web_result.h
index 969926674..8bfa2141d 100644
--- a/src/common/web_result.h
+++ b/src/common/web_result.h
@@ -5,6 +5,7 @@
#pragma once
#include <string>
+#include "common/common_types.h"
namespace Common {
struct WebResult {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e4a676e91..78986deb5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,6 +18,8 @@ add_library(core STATIC
crypto/encryption_layer.h
crypto/key_manager.cpp
crypto/key_manager.h
+ crypto/partition_data_manager.cpp
+ crypto/partition_data_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
@@ -70,6 +72,7 @@ add_library(core STATIC
file_sys/vfs_real.cpp
file_sys/vfs_real.h
file_sys/vfs_static.h
+ file_sys/vfs_types.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
@@ -397,8 +400,8 @@ create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
if (ENABLE_WEB_SERVICE)
- add_definitions(-DENABLE_WEB_SERVICE)
- target_link_libraries(core PUBLIC json-headers web_service)
+ target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
+ target_link_libraries(core PRIVATE web_service)
endif()
if (ARCHITECTURE_x86_64)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 0762321a9..4d2491870 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -144,7 +144,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
// Multi-process state
config.processor_id = core_index;
- config.global_monitor = &exclusive_monitor->monitor;
+ config.global_monitor = &exclusive_monitor.monitor;
// System registers
config.tpidrro_el0 = &cb->tpidrro_el0;
@@ -171,10 +171,9 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
-ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
- std::size_t core_index)
+ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
- exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
+ exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
ThreadContext ctx{};
inner_unicorn.SaveContext(ctx);
PageTableChanged();
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 4ee92ee27..512bf8ce9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -23,7 +23,7 @@ class DynarmicExclusiveMonitor;
class ARM_Dynarmic final : public ARM_Interface {
public:
- ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index);
+ ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,7 +62,7 @@ private:
ARM_Unicorn inner_unicorn;
std::size_t core_index;
- std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
+ DynarmicExclusiveMonitor& exclusive_monitor;
Memory::PageTable* current_page_table = nullptr;
};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e2fb9e038..3c57a62ec 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -71,9 +71,9 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
}
/// Runs a CPU core while the system is powered on
-void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
+void RunCpuCore(Cpu& cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
- cpu_state->RunLoop(true);
+ cpu_state.RunLoop(true);
}
}
} // Anonymous namespace
@@ -95,7 +95,7 @@ struct System::Impl {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
- thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
+ thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
@@ -139,16 +139,16 @@ struct System::Impl {
auto main_process = Kernel::Process::Create(kernel, "main");
kernel.MakeCurrentProcess(main_process.get());
- cpu_barrier = std::make_shared<CpuBarrier>();
+ cpu_barrier = std::make_unique<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
- cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
+ cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
- Service::Init(service_manager, virtual_filesystem);
+ Service::Init(service_manager, *virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
@@ -160,12 +160,12 @@ struct System::Impl {
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
- thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
+ thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
if (Settings::values.use_multi_core) {
for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
- std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
- thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
+ std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
+ thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
}
}
@@ -245,6 +245,7 @@ struct System::Impl {
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
+ cpu_exclusive_monitor.reset();
cpu_barrier.reset();
// Shutdown kernel and core timing
@@ -282,9 +283,9 @@ struct System::Impl {
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
- std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
- std::shared_ptr<CpuBarrier> cpu_barrier;
- std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
+ std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
+ std::unique_ptr<CpuBarrier> cpu_barrier;
+ std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
std::size_t active_core{}; ///< Active core, only used in single thread mode
@@ -298,7 +299,7 @@ struct System::Impl {
std::string status_details = "";
/// Map of guest threads to CPU cores
- std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
+ std::map<std::thread::id, Cpu*> thread_to_cpu;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
@@ -354,12 +355,15 @@ std::size_t System::CurrentCoreIndex() {
}
Kernel::Scheduler& System::CurrentScheduler() {
- return *CurrentCpuCore().Scheduler();
+ return CurrentCpuCore().Scheduler();
}
-const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) {
- ASSERT(core_index < NUM_CPU_CORES);
- return impl->cpu_cores[core_index]->Scheduler();
+Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
+ return CpuCore(core_index).Scheduler();
+}
+
+const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
+ return CpuCore(core_index).Scheduler();
}
Kernel::Process* System::CurrentProcess() {
@@ -380,6 +384,11 @@ Cpu& System::CpuCore(std::size_t core_index) {
return *impl->cpu_cores[core_index];
}
+const Cpu& System::CpuCore(std::size_t core_index) const {
+ ASSERT(core_index < NUM_CPU_CORES);
+ return *impl->cpu_cores[core_index];
+}
+
ExclusiveMonitor& System::Monitor() {
return *impl->cpu_exclusive_monitor;
}
diff --git a/src/core/core.h b/src/core/core.h
index ea4d53914..173be45f8 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -156,6 +156,9 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(std::size_t core_index);
+ /// Gets a CPU interface to the CPU core with the specified index
+ const Cpu& CpuCore(std::size_t core_index) const;
+
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor();
@@ -172,7 +175,10 @@ public:
const VideoCore::RendererBase& Renderer() const;
/// Gets the scheduler for the CPU core with the specified index
- const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index);
+ Kernel::Scheduler& Scheduler(std::size_t core_index);
+
+ /// Gets the scheduler for the CPU core with the specified index
+ const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
/// Provides a pointer to the current process
Kernel::Process* CurrentProcess();
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 265f8ed9c..fffda8a99 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -49,10 +49,8 @@ bool CpuBarrier::Rendezvous() {
return false;
}
-Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
- std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index)
- : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
-
+Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
+ : cpu_barrier{cpu_barrier}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
@@ -64,15 +62,15 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
arm_interface = std::make_unique<ARM_Unicorn>();
}
- scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface);
+ scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
}
Cpu::~Cpu() = default;
-std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
+std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
- return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
+ return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
#else
return nullptr; // TODO(merry): Passthrough exclusive monitor
#endif
@@ -83,7 +81,7 @@ std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_core
void Cpu::RunLoop(bool tight_loop) {
// Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
- if (!cpu_barrier->Rendezvous()) {
+ if (!cpu_barrier.Rendezvous()) {
// If rendezvous failed, session has been killed
return;
}
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index ee7e04abc..1d2bdc6cd 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -41,8 +41,7 @@ private:
class Cpu {
public:
- Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
- std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
+ Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
~Cpu();
void RunLoop(bool tight_loop = true);
@@ -59,8 +58,12 @@ public:
return *arm_interface;
}
- const std::shared_ptr<Kernel::Scheduler>& Scheduler() const {
- return scheduler;
+ Kernel::Scheduler& Scheduler() {
+ return *scheduler;
+ }
+
+ const Kernel::Scheduler& Scheduler() const {
+ return *scheduler;
}
bool IsMainCore() const {
@@ -71,14 +74,14 @@ public:
return core_index;
}
- static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
+ static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
private:
void Reschedule();
std::unique_ptr<ARM_Interface> arm_interface;
- std::shared_ptr<CpuBarrier> cpu_barrier;
- std::shared_ptr<Kernel::Scheduler> scheduler;
+ CpuBarrier& cpu_barrier;
+ std::unique_ptr<Kernel::Scheduler> scheduler;
std::atomic<bool> reschedule_pending = false;
std::size_t core_index;
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index bf3a70944..fd0786068 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -4,23 +4,56 @@
#include <algorithm>
#include <array>
+#include <bitset>
+#include <cctype>
#include <fstream>
#include <locale>
+#include <map>
#include <sstream>
#include <string_view>
#include <tuple>
#include <vector>
+#include <mbedtls/bignum.h>
+#include <mbedtls/cipher.h>
+#include <mbedtls/cmac.h>
+#include <mbedtls/sha256.h>
+#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
+#include "core/crypto/partition_data_manager.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/partition_filesystem.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace Core::Crypto {
+constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
+
+using namespace Common;
+
+const std::array<SHA256Hash, 2> eticket_source_hashes{
+ "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
+ "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
+};
+
+const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
+ {{S128KeyType::Master, 0}, "master_key_"},
+ {{S128KeyType::Package1, 0}, "package1_key_"},
+ {{S128KeyType::Package2, 0}, "package2_key_"},
+ {{S128KeyType::Titlekek, 0}, "titlekek_"},
+ {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
+ {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
+ {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
+};
+
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
Key128 out{};
@@ -37,57 +70,136 @@ Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, K
return out;
}
+Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source) {
+ AESCipher<Key128> sbk_cipher(sbk, Mode::ECB);
+ AESCipher<Key128> tsec_cipher(tsec, Mode::ECB);
+ tsec_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
+ sbk_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
+ return source;
+}
+
+Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source) {
+ Key128 master_root;
+ std::memcpy(master_root.data(), keyblob.data(), sizeof(Key128));
+
+ AESCipher<Key128> master_cipher(master_root, Mode::ECB);
+
+ Key128 master{};
+ master_cipher.Transcode(master_source.data(), master_source.size(), master.data(), Op::Decrypt);
+ return master;
+}
+
+std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
+ const Key128& key) {
+ std::array<u8, 0x90> keyblob;
+ AESCipher<Key128> cipher(key, Mode::CTR);
+ cipher.SetIV(std::vector<u8>(encrypted_keyblob.data() + 0x10, encrypted_keyblob.data() + 0x20));
+ cipher.Transcode(encrypted_keyblob.data() + 0x20, keyblob.size(), keyblob.data(), Op::Decrypt);
+ return keyblob;
+}
+
+void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
+ const auto kek_generation_source =
+ GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
+ const auto key_generation_source =
+ GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
+
+ if (HasKey(S128KeyType::Master, crypto_revision)) {
+ for (auto kak_type :
+ {KeyAreaKeyType::Application, KeyAreaKeyType::Ocean, KeyAreaKeyType::System}) {
+ if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(kak_type))) {
+ const auto source =
+ GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(kak_type));
+ const auto kek =
+ GenerateKeyEncryptionKey(source, GetKey(S128KeyType::Master, crypto_revision),
+ kek_generation_source, key_generation_source);
+ SetKey(S128KeyType::KeyArea, kek, crypto_revision, static_cast<u64>(kak_type));
+ }
+ }
+
+ AESCipher<Key128> master_cipher(GetKey(S128KeyType::Master, crypto_revision), Mode::ECB);
+ for (auto key_type : {SourceKeyType::Titlekek, SourceKeyType::Package2}) {
+ if (HasKey(S128KeyType::Source, static_cast<u64>(key_type))) {
+ Key128 key{};
+ master_cipher.Transcode(
+ GetKey(S128KeyType::Source, static_cast<u64>(key_type)).data(), key.size(),
+ key.data(), Op::Decrypt);
+ SetKey(key_type == SourceKeyType::Titlekek ? S128KeyType::Titlekek
+ : S128KeyType::Package2,
+ key, crypto_revision);
+ }
+ }
+ }
+}
+
+Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
+ AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
+ Key128 mac_key{};
+ mac_cipher.Transcode(mac_source.data(), mac_key.size(), mac_key.data(), Op::Decrypt);
+ return mac_key;
+}
+
boost::optional<Key128> DeriveSDSeed() {
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000043",
"rb+");
if (!save_43.IsOpen())
return boost::none;
+
const FileUtil::IOFile sd_private(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
if (!sd_private.IsOpen())
return boost::none;
- sd_private.Seek(0, SEEK_SET);
std::array<u8, 0x10> private_seed{};
- if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10)
+ if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
return boost::none;
+ }
std::array<u8, 0x10> buffer{};
std::size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
- save_43.Seek(offset, SEEK_SET);
+ if (!save_43.Seek(offset, SEEK_SET)) {
+ return boost::none;
+ }
+
save_43.ReadBytes(buffer.data(), buffer.size());
- if (buffer == private_seed)
+ if (buffer == private_seed) {
break;
+ }
}
- if (offset + 0x10 >= save_43.GetSize())
+ if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
return boost::none;
+ }
Key128 seed{};
- save_43.Seek(offset + 0x10, SEEK_SET);
- save_43.ReadBytes(seed.data(), seed.size());
+ if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
+ return boost::none;
+ }
return seed;
}
-Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) {
- if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)))
+Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
+ if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)))
return Loader::ResultStatus::ErrorMissingSDKEKSource;
- if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)))
+ if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)))
return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
const auto sd_kek_source =
- keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK));
+ keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
const auto aes_kek_gen =
- keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration));
+ keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
const auto aes_key_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
const auto master_00 = keys.GetKey(S128KeyType::Master);
const auto sd_kek =
GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
+ keys.SetKey(S128KeyType::SDKek, sd_kek);
if (!keys.HasKey(S128KeyType::SDSeed))
return Loader::ResultStatus::ErrorMissingSDSeed;
@@ -118,9 +230,147 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
return source; ///< Return unaltered source to satisfy output requirement.
});
+ keys.SetKey(S256KeyType::SDKey, sd_keys[0], static_cast<u64>(SDKeyType::Save));
+ keys.SetKey(S256KeyType::SDKey, sd_keys[1], static_cast<u64>(SDKeyType::NCA));
+
return Loader::ResultStatus::Success;
}
+std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
+ if (!ticket_save.IsOpen())
+ return {};
+
+ std::vector<u8> buffer(ticket_save.GetSize());
+ if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
+ return {};
+ }
+
+ std::vector<TicketRaw> out;
+ u32 magic{};
+ for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
+ if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
+ buffer[offset + 3] == 0x0) {
+ out.emplace_back();
+ auto& next = out.back();
+ std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
+ offset += next.size();
+ }
+ }
+
+ return out;
+}
+
+template <size_t size>
+static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
+ const std::array<u8, size>& rhs) {
+ std::array<u8, size> out{};
+ std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(), std::bit_xor<>());
+ return out;
+}
+
+template <size_t target_size, size_t in_size>
+static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
+ // Avoids truncation overflow within the loop below.
+ static_assert(target_size <= 0xFF);
+
+ std::array<u8, in_size + 4> seed_exp{};
+ std::memcpy(seed_exp.data(), seed.data(), in_size);
+
+ std::vector<u8> out;
+ size_t i = 0;
+ while (out.size() < target_size) {
+ out.resize(out.size() + 0x20);
+ seed_exp[in_size + 3] = static_cast<u8>(i);
+ mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
+ ++i;
+ }
+
+ std::array<u8, target_size> target;
+ std::memcpy(target.data(), out.data(), target_size);
+ return target;
+}
+
+template <size_t size>
+static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
+ u64 offset = 0;
+ for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
+ if (data[i] == 0x1) {
+ offset = i + 1;
+ break;
+ } else if (data[i] != 0x0) {
+ return boost::none;
+ }
+ }
+
+ return offset;
+}
+
+boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
+ const RSAKeyPair<2048>& key) {
+ u32 cert_authority;
+ std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
+ if (cert_authority == 0)
+ return boost::none;
+ if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
+ LOG_INFO(Crypto,
+ "Attempting to parse ticket with non-standard certificate authority {:08X}.",
+ cert_authority);
+ }
+
+ Key128 rights_id;
+ std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
+
+ if (rights_id == Key128{})
+ return boost::none;
+
+ Key128 key_temp{};
+
+ if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
+ std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
+ return std::make_pair(rights_id, key_temp);
+ }
+
+ mbedtls_mpi D; // RSA Private Exponent
+ mbedtls_mpi N; // RSA Modulus
+ mbedtls_mpi S; // Input
+ mbedtls_mpi M; // Output
+
+ mbedtls_mpi_init(&D);
+ mbedtls_mpi_init(&N);
+ mbedtls_mpi_init(&S);
+ mbedtls_mpi_init(&M);
+
+ mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
+ mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
+ mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
+
+ mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
+
+ std::array<u8, 0x100> rsa_step;
+ mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
+
+ u8 m_0 = rsa_step[0];
+ std::array<u8, 0x20> m_1;
+ std::memcpy(m_1.data(), rsa_step.data() + 0x01, m_1.size());
+ std::array<u8, 0xDF> m_2;
+ std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
+
+ if (m_0 != 0)
+ return boost::none;
+
+ m_1 = m_1 ^ MGF1<0x20>(m_2);
+ m_2 = m_2 ^ MGF1<0xDF>(m_1);
+
+ const auto offset = FindTicketOffset(m_2);
+ if (offset == boost::none)
+ return boost::none;
+ ASSERT(offset.get() > 0);
+
+ std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size());
+
+ return std::make_pair(rights_id, key_temp);
+}
+
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -137,6 +387,15 @@ KeyManager::KeyManager() {
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
+ AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
+ AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
+}
+
+static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
+ if (base.size() < begin + length)
+ return false;
+ return std::all_of(base.begin() + begin, base.begin() + begin + length,
+ [](u8 c) { return std::isdigit(c); });
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -158,6 +417,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
+ if (out[0].compare(0, 1, "#") == 0)
+ continue;
+
if (is_title_keys) {
auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
u128 rights_id{};
@@ -174,6 +436,50 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
const auto index = s256_file_id.at(out[0]);
Key256 key = Common::HexStringToArray<32>(out[1]);
s256_keys[{index.type, index.field1, index.field2}] = key;
+ } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
+ out[0].compare(0, 9, "keyblob_k") != 0) {
+ if (!ValidCryptoRevisionString(out[0], 8, 2))
+ continue;
+
+ const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
+ keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
+ } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
+ if (!ValidCryptoRevisionString(out[0], 18, 2))
+ continue;
+
+ const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
+ encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
+ } else {
+ for (const auto& kv : KEYS_VARIABLE_LENGTH) {
+ if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
+ continue;
+ if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
+ const auto index =
+ std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
+ const auto sub = kv.first.second;
+ if (sub == 0) {
+ s128_keys[{kv.first.first, index, 0}] =
+ Common::HexStringToArray<16>(out[1]);
+ } else {
+ s128_keys[{kv.first.first, kv.first.second, index}] =
+ Common::HexStringToArray<16>(out[1]);
+ }
+
+ break;
+ }
+ }
+
+ static constexpr std::array<const char*, 3> kak_names = {
+ "key_area_key_application_", "key_area_key_ocean_", "key_area_key_system_"};
+ for (size_t j = 0; j < kak_names.size(); ++j) {
+ const auto& match = kak_names[j];
+ if (out[0].compare(0, std::strlen(match), match) == 0) {
+ const auto index =
+ std::stoul(out[0].substr(std::strlen(match), 2), nullptr, 16);
+ s128_keys[{S128KeyType::KeyArea, index, j}] =
+ Common::HexStringToArray<16>(out[1]);
+ }
+ }
}
}
}
@@ -187,6 +493,28 @@ void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string&
LoadFromFile(dir2 + DIR_SEP + filename, title);
}
+bool KeyManager::BaseDeriveNecessary() const {
+ const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
+ return !HasKey(key_type, index1, index2);
+ };
+
+ if (check_key_existence(S256KeyType::Header))
+ return true;
+
+ for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
+ if (check_key_existence(S128KeyType::Master, i) ||
+ check_key_existence(S128KeyType::KeyArea, i,
+ static_cast<u64>(KeyAreaKeyType::Application)) ||
+ check_key_existence(S128KeyType::KeyArea, i, static_cast<u64>(KeyAreaKeyType::Ocean)) ||
+ check_key_existence(S128KeyType::KeyArea, i,
+ static_cast<u64>(KeyAreaKeyType::System)) ||
+ check_key_existence(S128KeyType::Titlekek, i))
+ return true;
+ }
+
+ return false;
+}
+
bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
return s128_keys.find({id, field1, field2}) != s128_keys.end();
}
@@ -207,13 +535,30 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.at({id, field1, field2});
}
-template <std::size_t Size>
-void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
+Key256 KeyManager::GetBISKey(u8 partition_id) const {
+ Key256 out{};
+
+ for (const auto& bis_type : {BISKeyType::Crypto, BISKeyType::Tweak}) {
+ if (HasKey(S128KeyType::BIS, partition_id, static_cast<u64>(bis_type))) {
+ std::memcpy(
+ out.data() + sizeof(Key128) * static_cast<u64>(bis_type),
+ s128_keys.at({S128KeyType::BIS, partition_id, static_cast<u64>(bis_type)}).data(),
+ sizeof(Key128));
+ }
+ }
+
+ return out;
+}
+
+template <size_t Size>
+void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key) {
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
std::string filename = "title.keys_autogenerated";
- if (!title_key)
+ if (category == KeyCategory::Standard)
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
+ else if (category == KeyCategory::Console)
+ filename = "console.keys_autogenerated";
const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
@@ -227,7 +572,7 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
}
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
- AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key);
+ AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
@@ -237,8 +582,15 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
- WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
+ WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
}
+
+ auto category = KeyCategory::Standard;
+ if (id == S128KeyType::Keyblob || id == S128KeyType::KeyblobMAC || id == S128KeyType::TSEC ||
+ id == S128KeyType::SecureBoot || id == S128KeyType::SDSeed || id == S128KeyType::BIS) {
+ category = KeyCategory::Console;
+ }
+
const auto iter2 = std::find_if(
s128_file_id.begin(), s128_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
@@ -246,7 +598,30 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
std::tie(id, field1, field2);
});
if (iter2 != s128_file_id.end())
- WriteKeyToFile(false, iter2->first, key);
+ WriteKeyToFile(category, iter2->first, key);
+
+ // Variable cases
+ if (id == S128KeyType::KeyArea) {
+ static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
+ "key_area_key_ocean_{:02X}",
+ "key_area_key_system_{:02X}"};
+ WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
+ } else if (id == S128KeyType::Master) {
+ WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
+ } else if (id == S128KeyType::Package1) {
+ WriteKeyToFile(category, fmt::format("package1_key_{:02X}", field1), key);
+ } else if (id == S128KeyType::Package2) {
+ WriteKeyToFile(category, fmt::format("package2_key_{:02X}", field1), key);
+ } else if (id == S128KeyType::Titlekek) {
+ WriteKeyToFile(category, fmt::format("titlekek_{:02X}", field1), key);
+ } else if (id == S128KeyType::Keyblob) {
+ WriteKeyToFile(category, fmt::format("keyblob_key_{:02X}", field1), key);
+ } else if (id == S128KeyType::KeyblobMAC) {
+ WriteKeyToFile(category, fmt::format("keyblob_mac_key_{:02X}", field1), key);
+ } else if (id == S128KeyType::Source && field1 == static_cast<u64>(SourceKeyType::Keyblob)) {
+ WriteKeyToFile(category, fmt::format("keyblob_key_source_{:02X}", field2), key);
+ }
+
s128_keys[{id, field1, field2}] = key;
}
@@ -260,7 +635,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
std::tie(id, field1, field2);
});
if (iter != s256_file_id.end())
- WriteKeyToFile(false, iter->first, key);
+ WriteKeyToFile(KeyCategory::Standard, iter->first, key);
s256_keys[{id, field1, field2}] = key;
}
@@ -290,59 +665,388 @@ void KeyManager::DeriveSDSeedLazy() {
SetKey(S128KeyType::SDSeed, res.get());
}
+static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
+ Key128 out{};
+
+ mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(),
+ key.size() * 8, source, size, out.data());
+ return out;
+}
+
+void KeyManager::DeriveBase() {
+ if (!BaseDeriveNecessary())
+ return;
+
+ if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
+ return;
+
+ const auto has_bis = [this](u64 id) {
+ return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
+ HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Tweak));
+ };
+
+ const auto copy_bis = [this](u64 id_from, u64 id_to) {
+ SetKey(S128KeyType::BIS,
+ GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Crypto)), id_to,
+ static_cast<u64>(BISKeyType::Crypto));
+
+ SetKey(S128KeyType::BIS,
+ GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Tweak)), id_to,
+ static_cast<u64>(BISKeyType::Tweak));
+ };
+
+ if (has_bis(2) && !has_bis(3))
+ copy_bis(2, 3);
+ else if (has_bis(3) && !has_bis(2))
+ copy_bis(3, 2);
+
+ std::bitset<32> revisions(0xFFFFFFFF);
+ for (size_t i = 0; i < revisions.size(); ++i) {
+ if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i) ||
+ encrypted_keyblobs[i] == std::array<u8, 0xB0>{}) {
+ revisions.reset(i);
+ }
+ }
+
+ if (!revisions.any())
+ return;
+
+ const auto sbk = GetKey(S128KeyType::SecureBoot);
+ const auto tsec = GetKey(S128KeyType::TSEC);
+ const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
+
+ for (size_t i = 0; i < revisions.size(); ++i) {
+ if (!revisions[i])
+ continue;
+
+ // Derive keyblob key
+ const auto key = DeriveKeyblobKey(
+ sbk, tsec, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i));
+
+ SetKey(S128KeyType::Keyblob, key, i);
+
+ // Derive keyblob MAC key
+ if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
+ continue;
+
+ const auto mac_key = DeriveKeyblobMACKey(
+ key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
+ SetKey(S128KeyType::KeyblobMAC, mac_key, i);
+
+ Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
+ if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
+ continue;
+
+ // Decrypt keyblob
+ if (keyblobs[i] == std::array<u8, 0x90>{}) {
+ keyblobs[i] = DecryptKeyblob(encrypted_keyblobs[i], key);
+ WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
+ keyblobs[i]);
+ }
+
+ Key128 package1;
+ std::memcpy(package1.data(), keyblobs[i].data() + 0x80, sizeof(Key128));
+ SetKey(S128KeyType::Package1, package1, i);
+
+ // Derive master key
+ if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master))) {
+ SetKey(S128KeyType::Master,
+ DeriveMasterKey(keyblobs[i], GetKey(S128KeyType::Source,
+ static_cast<u64>(SourceKeyType::Master))),
+ i);
+ }
+ }
+
+ revisions.set();
+ for (size_t i = 0; i < revisions.size(); ++i) {
+ if (!HasKey(S128KeyType::Master, i))
+ revisions.reset(i);
+ }
+
+ if (!revisions.any())
+ return;
+
+ for (size_t i = 0; i < revisions.size(); ++i) {
+ if (!revisions[i])
+ continue;
+
+ // Derive general purpose keys
+ DeriveGeneralPurposeKeys(i);
+ }
+
+ if (HasKey(S128KeyType::Master, 0) &&
+ HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)) &&
+ HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)) &&
+ HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)) &&
+ HasKey(S256KeyType::HeaderSource)) {
+ const auto header_kek = GenerateKeyEncryptionKey(
+ GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)),
+ GetKey(S128KeyType::Master, 0),
+ GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)),
+ GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)));
+ SetKey(S128KeyType::HeaderKek, header_kek);
+
+ AESCipher<Key128> header_cipher(header_kek, Mode::ECB);
+ Key256 out = GetKey(S256KeyType::HeaderSource);
+ header_cipher.Transcode(out.data(), out.size(), out.data(), Op::Decrypt);
+ SetKey(S256KeyType::Header, out);
+ }
+}
+
+void KeyManager::DeriveETicket(PartitionDataManager& data) {
+ // ETicket keys
+ const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
+ 0x0100000000000033, FileSys::ContentRecordType::Program);
+
+ if (es == nullptr)
+ return;
+
+ const auto exefs = es->GetExeFS();
+ if (exefs == nullptr)
+ return;
+
+ const auto main = exefs->GetFile("main");
+ if (main == nullptr)
+ return;
+
+ const auto bytes = main->ReadAllBytes();
+
+ const auto eticket_kek = FindKeyFromHex16(bytes, eticket_source_hashes[0]);
+ const auto eticket_kekek = FindKeyFromHex16(bytes, eticket_source_hashes[1]);
+
+ const auto seed3 = data.GetRSAKekSeed3();
+ const auto mask0 = data.GetRSAKekMask0();
+
+ if (eticket_kek != Key128{})
+ SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
+ if (eticket_kekek != Key128{}) {
+ SetKey(S128KeyType::Source, eticket_kekek,
+ static_cast<size_t>(SourceKeyType::ETicketKekek));
+ }
+ if (seed3 != Key128{})
+ SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
+ if (mask0 != Key128{})
+ SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
+ if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
+ mask0 == Key128{}) {
+ return;
+ }
+
+ Key128 rsa_oaep_kek{};
+ std::transform(seed3.begin(), seed3.end(), mask0.begin(), rsa_oaep_kek.begin(),
+ std::bit_xor<>());
+
+ if (rsa_oaep_kek == Key128{})
+ return;
+
+ SetKey(S128KeyType::Source, rsa_oaep_kek,
+ static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
+
+ Key128 temp_kek{};
+ Key128 temp_kekek{};
+ Key128 eticket_final{};
+
+ // Derive ETicket RSA Kek
+ AESCipher<Key128> es_master(GetKey(S128KeyType::Master), Mode::ECB);
+ es_master.Transcode(rsa_oaep_kek.data(), rsa_oaep_kek.size(), temp_kek.data(), Op::Decrypt);
+ AESCipher<Key128> es_kekek(temp_kek, Mode::ECB);
+ es_kekek.Transcode(eticket_kekek.data(), eticket_kekek.size(), temp_kekek.data(), Op::Decrypt);
+ AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
+ es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
+
+ if (eticket_final == Key128{})
+ return;
+
+ SetKey(S128KeyType::ETicketRSAKek, eticket_final);
+
+ // Titlekeys
+ data.DecryptProdInfo(GetBISKey(0));
+
+ const auto eticket_extended_kek = data.GetETicketExtendedKek();
+
+ std::vector<u8> extended_iv(0x10);
+ std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
+ std::array<u8, 0x230> extended_dec{};
+ AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
+ rsa_1.SetIV(extended_iv);
+ rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
+ extended_dec.data(), Op::Decrypt);
+
+ RSAKeyPair<2048> rsa_key{};
+ std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
+ std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
+ std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
+
+ const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ "/system/save/80000000000000e1",
+ "rb+");
+ const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ "/system/save/80000000000000e2",
+ "rb+");
+
+ const auto blob2 = GetTicketblob(save2);
+ auto res = GetTicketblob(save1);
+ res.insert(res.end(), blob2.begin(), blob2.end());
+
+ for (const auto& raw : res) {
+ const auto pair = ParseTicket(raw, rsa_key);
+ if (pair == boost::none)
+ continue;
+ const auto& [rid, key] = pair.value();
+ u128 rights_id;
+ std::memcpy(rights_id.data(), rid.data(), rid.size());
+ SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
+ }
+}
+
+void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
+ if (key == Key128{})
+ return;
+ SetKey(id, key, field1, field2);
+}
+
+void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
+ if (key == Key256{})
+ return;
+ SetKey(id, key, field1, field2);
+}
+
+void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
+ if (!BaseDeriveNecessary())
+ return;
+
+ if (!data.HasBoot0())
+ return;
+
+ for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
+ if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{})
+ continue;
+ encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
+ WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
+ encrypted_keyblobs[i]);
+ }
+
+ SetKeyWrapped(S128KeyType::Source, data.GetPackage2KeySource(),
+ static_cast<u64>(SourceKeyType::Package2));
+ SetKeyWrapped(S128KeyType::Source, data.GetAESKekGenerationSource(),
+ static_cast<u64>(SourceKeyType::AESKekGeneration));
+ SetKeyWrapped(S128KeyType::Source, data.GetTitlekekSource(),
+ static_cast<u64>(SourceKeyType::Titlekek));
+ SetKeyWrapped(S128KeyType::Source, data.GetMasterKeySource(),
+ static_cast<u64>(SourceKeyType::Master));
+ SetKeyWrapped(S128KeyType::Source, data.GetKeyblobMACKeySource(),
+ static_cast<u64>(SourceKeyType::KeyblobMAC));
+
+ for (size_t i = 0; i < PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH; ++i) {
+ SetKeyWrapped(S128KeyType::Source, data.GetKeyblobKeySource(i),
+ static_cast<u64>(SourceKeyType::Keyblob), i);
+ }
+
+ if (data.HasFuses())
+ SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
+
+ DeriveBase();
+
+ Key128 latest_master{};
+ for (s8 i = 0x1F; i >= 0; --i) {
+ if (GetKey(S128KeyType::Master, static_cast<u8>(i)) != Key128{}) {
+ latest_master = GetKey(S128KeyType::Master, static_cast<u8>(i));
+ break;
+ }
+ }
+
+ const auto masters = data.GetTZMasterKeys(latest_master);
+ for (size_t i = 0; i < masters.size(); ++i) {
+ if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i))
+ SetKey(S128KeyType::Master, masters[i], i);
+ }
+
+ DeriveBase();
+
+ if (!data.HasPackage2())
+ return;
+
+ std::array<Key128, 0x20> package2_keys{};
+ for (size_t i = 0; i < package2_keys.size(); ++i) {
+ if (HasKey(S128KeyType::Package2, i))
+ package2_keys[i] = GetKey(S128KeyType::Package2, i);
+ }
+ data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
+
+ SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyApplicationSource(),
+ static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::Application));
+ SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyOceanSource(),
+ static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::Ocean));
+ SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeySystemSource(),
+ static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::System));
+ SetKeyWrapped(S128KeyType::Source, data.GetSDKekSource(),
+ static_cast<u64>(SourceKeyType::SDKek));
+ SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDSaveKeySource(),
+ static_cast<u64>(SDKeyType::Save));
+ SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDNCAKeySource(),
+ static_cast<u64>(SDKeyType::NCA));
+ SetKeyWrapped(S128KeyType::Source, data.GetHeaderKekSource(),
+ static_cast<u64>(SourceKeyType::HeaderKek));
+ SetKeyWrapped(S256KeyType::HeaderSource, data.GetHeaderKeySource());
+ SetKeyWrapped(S128KeyType::Source, data.GetAESKeyGenerationSource(),
+ static_cast<u64>(SourceKeyType::AESKeyGeneration));
+
+ DeriveBase();
+}
+
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
- {"master_key_00", {S128KeyType::Master, 0, 0}},
- {"master_key_01", {S128KeyType::Master, 1, 0}},
- {"master_key_02", {S128KeyType::Master, 2, 0}},
- {"master_key_03", {S128KeyType::Master, 3, 0}},
- {"master_key_04", {S128KeyType::Master, 4, 0}},
- {"package1_key_00", {S128KeyType::Package1, 0, 0}},
- {"package1_key_01", {S128KeyType::Package1, 1, 0}},
- {"package1_key_02", {S128KeyType::Package1, 2, 0}},
- {"package1_key_03", {S128KeyType::Package1, 3, 0}},
- {"package1_key_04", {S128KeyType::Package1, 4, 0}},
- {"package2_key_00", {S128KeyType::Package2, 0, 0}},
- {"package2_key_01", {S128KeyType::Package2, 1, 0}},
- {"package2_key_02", {S128KeyType::Package2, 2, 0}},
- {"package2_key_03", {S128KeyType::Package2, 3, 0}},
- {"package2_key_04", {S128KeyType::Package2, 4, 0}},
- {"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
- {"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
- {"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
- {"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
- {"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
- {"key_area_key_application_00",
- {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}},
- {"key_area_key_application_01",
- {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}},
- {"key_area_key_application_02",
- {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}},
- {"key_area_key_application_03",
- {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}},
- {"key_area_key_application_04",
- {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
- {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
- {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
- {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
- {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
- {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
- {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
- {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
- {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
- {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
- {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
- {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
+ {"eticket_rsa_kek_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
+ {"eticket_rsa_kekek_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
+ {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
+ {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
+ {"rsa_oaep_kek_generation_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
+ {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
{"aes_kek_generation_source",
- {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}},
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
{"aes_key_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
+ {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
+ {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
+ {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
+ {"key_area_key_application_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::Application)}},
+ {"key_area_key_ocean_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::Ocean)}},
+ {"key_area_key_system_source",
+ {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+ static_cast<u64>(KeyAreaKeyType::System)}},
+ {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
+ {"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}},
+ {"tsec_key", {S128KeyType::TSEC, 0, 0}},
+ {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
{"sd_seed", {S128KeyType::SDSeed, 0, 0}},
+ {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
+ {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
+ {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
+ {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
+ {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
+ {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
+ {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
};
const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
{"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
+ {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
+ {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
+ {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
};
} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 978eec8dc..cccb3c0ae 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -5,11 +5,18 @@
#pragma once
#include <array>
+#include <map>
#include <string>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/common_types.h"
+#include "core/crypto/partition_data_manager.h"
+#include "core/file_sys/vfs_types.h"
+
+namespace FileUtil {
+class IOFile;
+}
namespace Loader {
enum class ResultStatus : u16;
@@ -22,13 +29,30 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
+using TicketRaw = std::array<u8, 0x400>;
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
-static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
+static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
+
+template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
+struct RSAKeyPair {
+ std::array<u8, byte_size> encryption_key;
+ std::array<u8, byte_size> decryption_key;
+ std::array<u8, byte_size> modulus;
+ std::array<u8, 4> exponent;
+};
+
+enum class KeyCategory : u8 {
+ Standard,
+ Title,
+ Console,
+};
enum class S256KeyType : u64 {
- Header, //
- SDKeySource, // f1=SDKeyType
+ SDKey, // f1=SDKeyType
+ Header, //
+ SDKeySource, // f1=SDKeyType
+ HeaderSource, //
};
enum class S128KeyType : u64 {
@@ -41,6 +65,14 @@ enum class S128KeyType : u64 {
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
Source, // f1=source type, f2= sub id
+ Keyblob, // f1=crypto revision
+ KeyblobMAC, // f1=crypto revision
+ TSEC, //
+ SecureBoot, //
+ BIS, // f1=partition (0-3), f2=type {crypt, tweak}
+ HeaderKek, //
+ SDKek, //
+ RSAKek, //
};
enum class KeyAreaKeyType : u8 {
@@ -50,9 +82,19 @@ enum class KeyAreaKeyType : u8 {
};
enum class SourceKeyType : u8 {
- SDKEK,
- AESKEKGeneration,
- AESKeyGeneration,
+ SDKek, //
+ AESKekGeneration, //
+ AESKeyGeneration, //
+ RSAOaepKekGeneration, //
+ Master, //
+ Keyblob, // f2=crypto revision
+ KeyAreaKey, // f2=KeyAreaKeyType
+ Titlekek, //
+ Package2, //
+ HeaderKek, //
+ KeyblobMAC, //
+ ETicketKek, //
+ ETicketKekek, //
};
enum class SDKeyType : u8 {
@@ -60,6 +102,16 @@ enum class SDKeyType : u8 {
NCA,
};
+enum class BISKeyType : u8 {
+ Crypto,
+ Tweak,
+};
+
+enum class RSAKekType : u8 {
+ Mask0,
+ Seed3,
+};
+
template <typename KeyType>
struct KeyIndex {
KeyType type;
@@ -91,6 +143,8 @@ public:
Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
+ Key256 GetBISKey(u8 partition_id) const;
+
void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
@@ -100,23 +154,51 @@ public:
// 8*43 and the private file to exist.
void DeriveSDSeedLazy();
+ bool BaseDeriveNecessary() const;
+ void DeriveBase();
+ void DeriveETicket(PartitionDataManager& data);
+
+ void PopulateFromPartitionData(PartitionDataManager& data);
+
private:
- boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys;
- boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys;
+ std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
+ std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
+
+ std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
+ std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
- template <std::size_t Size>
- void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);
+ template <size_t Size>
+ void WriteKeyToFile(KeyCategory category, std::string_view keyname,
+ const std::array<u8, Size>& key);
+
+ void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
+
+ void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
+ void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
+Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source);
+Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source);
+Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source);
+std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
+ const Key128& key);
+
boost::optional<Key128> DeriveSDSeed();
-Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys);
+Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
+
+std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
+
+// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
+// 0x140-0x144 is zero)
+boost::optional<std::pair<Key128, Key128>> ParseTicket(
+ const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
new file mode 100644
index 000000000..25cee1f3a
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -0,0 +1,593 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// NOTE TO FUTURE MAINTAINERS:
+// When a new version of switch cryptography is released,
+// hash the new keyblob source and master key and add the hashes to
+// the arrays below.
+
+#include <algorithm>
+#include <array>
+#include <cctype>
+#include <cstring>
+#include <mbedtls/sha256.h>
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/hex_util.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "common/swap.h"
+#include "core/crypto/key_manager.h"
+#include "core/crypto/partition_data_manager.h"
+#include "core/crypto/xts_encryption_layer.h"
+#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_offset.h"
+
+using namespace Common;
+
+namespace Core::Crypto {
+
+struct Package2Header {
+ std::array<u8, 0x100> signature;
+ Key128 header_ctr;
+ std::array<Key128, 4> section_ctr;
+ u32_le magic;
+ u32_le base_offset;
+ INSERT_PADDING_BYTES(4);
+ u8 version_max;
+ u8 version_min;
+ INSERT_PADDING_BYTES(2);
+ std::array<u32_le, 4> section_size;
+ std::array<u32_le, 4> section_offset;
+ std::array<SHA256Hash, 4> section_hash;
+};
+static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
+
+struct INIHeader {
+ u32_le magic;
+ u32_le size;
+ u32_le process_count;
+ INSERT_PADDING_BYTES(4);
+};
+static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
+
+struct SectionHeader {
+ u32_le offset;
+ u32_le size_decompressed;
+ u32_le size_compressed;
+ u32_le attribute;
+};
+static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
+
+struct KIPHeader {
+ u32_le magic;
+ std::array<char, 12> name;
+ u64_le title_id;
+ u32_le category;
+ u8 priority;
+ u8 core;
+ INSERT_PADDING_BYTES(1);
+ u8 flags;
+ std::array<SectionHeader, 6> sections;
+ std::array<u32, 0x20> capabilities;
+};
+static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
+
+const std::array<SHA256Hash, 0x10> source_hashes{
+ "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
+ "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
+ "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
+ "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
+ "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
+ "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
+ "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
+ "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
+ "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
+ "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
+ "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
+ "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
+ "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
+ "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
+ "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
+ "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
+};
+
+const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
+ "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
+ "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
+ "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
+ "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
+ "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
+ "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
+
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
+
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
+
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
+};
+
+const std::array<SHA256Hash, 0x20> master_key_hashes{
+ "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
+ "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
+ "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
+ "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
+ "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
+ "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
+
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
+
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
+
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
+ "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
+};
+
+static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
+ const auto data_size = in.size() - 0xC;
+
+ u32 compressed_size{};
+ u32 init_index{};
+ u32 additional_size{};
+ std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
+ std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
+ std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
+
+ std::vector<u8> out(in.size() + additional_size);
+
+ if (compressed_size == in.size())
+ std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
+ else
+ std::memcpy(out.data(), in.data(), compressed_size);
+
+ auto index = in.size() - init_index;
+ auto out_index = out.size();
+
+ while (out_index > 0) {
+ --index;
+ auto control = in[index];
+ for (size_t i = 0; i < 8; ++i) {
+ if ((control & 0x80) > 0) {
+ ASSERT(index >= 2);
+ index -= 2;
+ u64 segment_offset = in[index] | in[index + 1] << 8;
+ u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
+ segment_offset &= 0xFFF;
+ segment_offset += 3;
+
+ if (out_index < segment_size)
+ segment_size = out_index;
+
+ ASSERT(out_index >= segment_size);
+
+ out_index -= segment_size;
+
+ for (size_t j = 0; j < segment_size; ++j) {
+ ASSERT(out_index + j + segment_offset < out.size());
+ out[out_index + j] = out[out_index + j + segment_offset];
+ }
+ } else {
+ ASSERT(out_index >= 1);
+ --out_index;
+ --index;
+ out[out_index] = in[index];
+ }
+
+ control <<= 1;
+ if (out_index == 0)
+ return out;
+ }
+ }
+
+ return out;
+}
+
+static u8 CalculateMaxKeyblobSourceHash() {
+ for (s8 i = 0x1F; i >= 0; --i) {
+ if (keyblob_source_hashes[i] != SHA256Hash{})
+ return static_cast<u8>(i + 1);
+ }
+
+ return 0;
+}
+
+const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = CalculateMaxKeyblobSourceHash();
+
+template <size_t key_size = 0x10>
+std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary,
+ const std::array<u8, 0x20>& hash) {
+ if (binary.size() < key_size)
+ return {};
+
+ std::array<u8, 0x20> temp{};
+ for (size_t i = 0; i < binary.size() - key_size; ++i) {
+ mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
+
+ if (temp != hash)
+ continue;
+
+ std::array<u8, key_size> out{};
+ std::memcpy(out.data(), binary.data() + i, key_size);
+ return out;
+ }
+
+ return {};
+}
+
+std::array<u8, 16> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 32> hash) {
+ return FindKeyFromHex<0x10>(binary, hash);
+}
+
+static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<u8>& binary,
+ const Key128& key) {
+ if (binary.size() < 0x10)
+ return {};
+
+ SHA256Hash temp{};
+ Key128 dec_temp{};
+ std::array<Key128, 0x20> out{};
+ AESCipher<Key128> cipher(key, Mode::ECB);
+ for (size_t i = 0; i < binary.size() - 0x10; ++i) {
+ cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
+ mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
+
+ for (size_t k = 0; k < out.size(); ++k) {
+ if (temp == master_key_hashes[k]) {
+ out[k] = dec_temp;
+ break;
+ }
+ }
+ }
+
+ return out;
+}
+
+FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
+ const std::string& name) {
+ auto upper = name;
+ std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
+ for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
+ if (dir->GetFile(fname) != nullptr)
+ return dir->GetFile(fname);
+ }
+
+ return nullptr;
+}
+
+PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
+ : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
+ fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
+ kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
+ package2({
+ FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
+ FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
+ FindFileInDirWithNames(sysdata_dir, "BCPKG2-3-SafeMode-Main"),
+ FindFileInDirWithNames(sysdata_dir, "BCPKG2-4-SafeMode-Sub"),
+ FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
+ FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
+ }),
+ prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
+ secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
+ package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
+ secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
+ : secure_monitor->ReadAllBytes()),
+ package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
+ : package1_decrypted->ReadAllBytes()) {
+}
+
+PartitionDataManager::~PartitionDataManager() = default;
+
+bool PartitionDataManager::HasBoot0() const {
+ return boot0 != nullptr;
+}
+
+FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
+ return boot0;
+}
+
+PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
+ std::size_t index) const {
+ if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
+ return GetEncryptedKeyblobs()[index];
+ return {};
+}
+
+PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
+ if (!HasBoot0())
+ return {};
+
+ EncryptedKeyBlobs out{};
+ for (size_t i = 0; i < out.size(); ++i)
+ boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
+ return out;
+}
+
+std::vector<u8> PartitionDataManager::GetSecureMonitor() const {
+ return secure_monitor_bytes;
+}
+
+std::array<u8, 16> PartitionDataManager::GetPackage2KeySource() const {
+ return FindKeyFromHex(secure_monitor_bytes, source_hashes[2]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetAESKekGenerationSource() const {
+ return FindKeyFromHex(secure_monitor_bytes, source_hashes[3]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetTitlekekSource() const {
+ return FindKeyFromHex(secure_monitor_bytes, source_hashes[5]);
+}
+
+std::array<std::array<u8, 16>, 32> PartitionDataManager::GetTZMasterKeys(
+ std::array<u8, 0x10> master_key) const {
+ return FindEncryptedMasterKeyFromHex(secure_monitor_bytes, master_key);
+}
+
+std::array<u8, 16> PartitionDataManager::GetRSAKekSeed3() const {
+ return FindKeyFromHex(secure_monitor_bytes, source_hashes[14]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetRSAKekMask0() const {
+ return FindKeyFromHex(secure_monitor_bytes, source_hashes[15]);
+}
+
+std::vector<u8> PartitionDataManager::GetPackage1Decrypted() const {
+ return package1_decrypted_bytes;
+}
+
+std::array<u8, 16> PartitionDataManager::GetMasterKeySource() const {
+ return FindKeyFromHex(package1_decrypted_bytes, source_hashes[1]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
+ return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
+ if (keyblob_source_hashes[revision] == SHA256Hash{}) {
+ LOG_WARNING(Crypto,
+ "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
+ revision);
+ }
+ return FindKeyFromHex(package1_decrypted_bytes, keyblob_source_hashes[revision]);
+}
+
+bool PartitionDataManager::HasFuses() const {
+ return fuses != nullptr;
+}
+
+FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const {
+ return fuses;
+}
+
+std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const {
+ if (!HasFuses())
+ return {};
+ Key128 out{};
+ fuses->Read(out.data(), out.size(), 0xA4);
+ return out;
+}
+
+bool PartitionDataManager::HasKFuses() const {
+ return kfuses != nullptr;
+}
+
+FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
+ return kfuses;
+}
+
+bool PartitionDataManager::HasPackage2(Package2Type type) const {
+ return package2.at(static_cast<size_t>(type)) != nullptr;
+}
+
+FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const {
+ return package2.at(static_cast<size_t>(type));
+}
+
+bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
+
+ const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
+ Package2Header temp = header;
+ AESCipher<Key128> cipher(key, Mode::CTR);
+ cipher.SetIV(iv);
+ cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
+ Op::Decrypt);
+ if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
+ header = temp;
+ return true;
+ }
+
+ return false;
+}
+
+void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
+ Package2Type type) {
+ FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
+ package2[static_cast<size_t>(type)],
+ package2[static_cast<size_t>(type)]->GetSize() - 0x4000, 0x4000);
+
+ Package2Header header{};
+ if (file->ReadObject(&header) != sizeof(Package2Header))
+ return;
+
+ std::size_t revision = 0xFF;
+ if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
+ for (std::size_t i = 0; i < package2_keys.size(); ++i) {
+ if (AttemptDecrypt(package2_keys[i], header)) {
+ revision = i;
+ }
+ }
+ }
+
+ if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
+ return;
+
+ const auto a = std::make_shared<FileSys::OffsetVfsFile>(
+ file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
+
+ auto c = a->ReadAllBytes();
+
+ AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
+ cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
+ cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
+
+ INIHeader ini;
+ std::memcpy(&ini, c.data(), sizeof(INIHeader));
+ if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
+ return;
+
+ u64 offset = sizeof(INIHeader);
+ for (size_t i = 0; i < ini.process_count; ++i) {
+ KIPHeader kip;
+ std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
+ if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
+ return;
+
+ const auto name =
+ Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
+
+ if (name != "FS" && name != "spl") {
+ offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
+ kip.sections[1].size_compressed + kip.sections[2].size_compressed;
+ continue;
+ }
+
+ const u64 initial_offset = sizeof(KIPHeader) + offset;
+ const auto text_begin = c.cbegin() + initial_offset;
+ const auto text_end = text_begin + kip.sections[0].size_compressed;
+ const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
+
+ const auto rodata_end = text_end + kip.sections[1].size_compressed;
+ const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
+
+ const auto data_end = rodata_end + kip.sections[2].size_compressed;
+ const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
+
+ std::vector<u8> out;
+ out.reserve(text.size() + rodata.size() + data.size());
+ out.insert(out.end(), text.begin(), text.end());
+ out.insert(out.end(), rodata.begin(), rodata.end());
+ out.insert(out.end(), data.begin(), data.end());
+
+ offset += sizeof(KIPHeader) + out.size();
+
+ if (name == "FS")
+ package2_fs[static_cast<size_t>(type)] = std::move(out);
+ else if (name == "spl")
+ package2_spl[static_cast<size_t>(type)] = std::move(out);
+ }
+}
+
+const std::vector<u8>& PartitionDataManager::GetPackage2FSDecompressed(Package2Type type) const {
+ return package2_fs.at(static_cast<size_t>(type));
+}
+
+std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyApplicationSource(Package2Type type) const {
+ return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[6]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyOceanSource(Package2Type type) const {
+ return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[7]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetKeyAreaKeySystemSource(Package2Type type) const {
+ return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[8]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetSDKekSource(Package2Type type) const {
+ return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[9]);
+}
+
+std::array<u8, 32> PartitionDataManager::GetSDSaveKeySource(Package2Type type) const {
+ return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[10]);
+}
+
+std::array<u8, 32> PartitionDataManager::GetSDNCAKeySource(Package2Type type) const {
+ return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[11]);
+}
+
+std::array<u8, 16> PartitionDataManager::GetHeaderKekSource(Package2Type type) const {
+ return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[12]);
+}
+
+std::array<u8, 32> PartitionDataManager::GetHeaderKeySource(Package2Type type) const {
+ return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[13]);
+}
+
+const std::vector<u8>& PartitionDataManager::GetPackage2SPLDecompressed(Package2Type type) const {
+ return package2_spl.at(static_cast<size_t>(type));
+}
+
+std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type type) const {
+ return FindKeyFromHex(package2_spl.at(static_cast<size_t>(type)), source_hashes[4]);
+}
+
+bool PartitionDataManager::HasProdInfo() const {
+ return prodinfo != nullptr;
+}
+
+FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const {
+ return prodinfo;
+}
+
+void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
+ if (prodinfo == nullptr)
+ return;
+
+ prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
+}
+
+std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
+ std::array<u8, 0x240> out{};
+ if (prodinfo_decrypted != nullptr)
+ prodinfo_decrypted->Read(out.data(), out.size(), 0x3890);
+ return out;
+}
+} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
new file mode 100644
index 000000000..0ad007c72
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.h
@@ -0,0 +1,109 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
+
+namespace Core::Crypto {
+
+enum class Package2Type {
+ NormalMain,
+ NormalSub,
+ SafeModeMain,
+ SafeModeSub,
+ RepairMain,
+ RepairSub,
+};
+
+class PartitionDataManager {
+public:
+ static const u8 MAX_KEYBLOB_SOURCE_HASH;
+ static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32;
+ static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0;
+
+ using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>;
+ using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
+
+ explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
+ ~PartitionDataManager();
+
+ // BOOT0
+ bool HasBoot0() const;
+ FileSys::VirtualFile GetBoot0Raw() const;
+ EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
+ EncryptedKeyBlobs GetEncryptedKeyblobs() const;
+ std::vector<u8> GetSecureMonitor() const;
+ std::array<u8, 0x10> GetPackage2KeySource() const;
+ std::array<u8, 0x10> GetAESKekGenerationSource() const;
+ std::array<u8, 0x10> GetTitlekekSource() const;
+ std::array<std::array<u8, 0x10>, 0x20> GetTZMasterKeys(std::array<u8, 0x10> master_key) const;
+ std::array<u8, 0x10> GetRSAKekSeed3() const;
+ std::array<u8, 0x10> GetRSAKekMask0() const;
+ std::vector<u8> GetPackage1Decrypted() const;
+ std::array<u8, 0x10> GetMasterKeySource() const;
+ std::array<u8, 0x10> GetKeyblobMACKeySource() const;
+ std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
+
+ // Fuses
+ bool HasFuses() const;
+ FileSys::VirtualFile GetFusesRaw() const;
+ std::array<u8, 0x10> GetSecureBootKey() const;
+
+ // K-Fuses
+ bool HasKFuses() const;
+ FileSys::VirtualFile GetKFusesRaw() const;
+
+ // Package2
+ bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
+ FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
+ void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
+ Package2Type type);
+ const std::vector<u8>& GetPackage2FSDecompressed(
+ Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
+ Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x10> GetKeyAreaKeyOceanSource(
+ Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x10> GetKeyAreaKeySystemSource(
+ Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x10> GetSDKekSource(Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x20> GetSDSaveKeySource(Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x20> GetSDNCAKeySource(Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x10> GetHeaderKekSource(Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x20> GetHeaderKeySource(Package2Type type = Package2Type::NormalMain) const;
+ const std::vector<u8>& GetPackage2SPLDecompressed(
+ Package2Type type = Package2Type::NormalMain) const;
+ std::array<u8, 0x10> GetAESKeyGenerationSource(
+ Package2Type type = Package2Type::NormalMain) const;
+
+ // PRODINFO
+ bool HasProdInfo() const;
+ FileSys::VirtualFile GetProdInfoRaw() const;
+ void DecryptProdInfo(std::array<u8, 0x20> bis_key);
+ std::array<u8, 0x240> GetETicketExtendedKek() const;
+
+private:
+ FileSys::VirtualFile boot0;
+ FileSys::VirtualFile fuses;
+ FileSys::VirtualFile kfuses;
+ std::array<FileSys::VirtualFile, 6> package2;
+ FileSys::VirtualFile prodinfo;
+ FileSys::VirtualFile secure_monitor;
+ FileSys::VirtualFile package1_decrypted;
+
+ // Processed
+ std::array<FileSys::VirtualFile, 6> package2_decrypted;
+ FileSys::VirtualFile prodinfo_decrypted;
+ std::vector<u8> secure_monitor_bytes;
+ std::vector<u8> package1_decrypted_bytes;
+ std::array<std::vector<u8>, 6> package2_fs;
+ std::array<std::vector<u8>, 6> package2_spl;
+};
+
+std::array<u8, 0x10> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 0x20> hash);
+
+} // namespace Core::Crypto
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 6102ef476..76a2b7e86 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -10,19 +10,19 @@ namespace FileSys {
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
- sysnand_cache(std::make_shared<RegisteredCache>(
+ sysnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
- usrnand_cache(std::make_shared<RegisteredCache>(
+ usrnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
BISFactory::~BISFactory() = default;
-std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
- return sysnand_cache;
+RegisteredCache* BISFactory::GetSystemNANDContents() const {
+ return sysnand_cache.get();
}
-std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
- return usrnand_cache;
+RegisteredCache* BISFactory::GetUserNANDContents() const {
+ return usrnand_cache.get();
}
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index c352e0925..364d309bd 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -20,8 +20,8 @@ public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
~BISFactory();
- std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
- std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
+ RegisteredCache* GetSystemNANDContents() const;
+ RegisteredCache* GetUserNANDContents() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
@@ -29,8 +29,8 @@ private:
VirtualDir nand_root;
VirtualDir load_root;
- std::shared_ptr<RegisteredCache> sysnand_cache;
- std::shared_ptr<RegisteredCache> usrnand_cache;
+ std::unique_ptr<RegisteredCache> sysnand_cache;
+ std::unique_ptr<RegisteredCache> usrnand_cache;
};
} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
};
std::string LanguageEntry::GetApplicationName() const {
- return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
+ return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
+ application_name.size());
}
std::string LanguageEntry::GetDeveloperName() const {
- return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100);
+ return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
+ developer_name.size());
}
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
return raw->title_id;
}
+u64 NACP::GetDLCBaseTitleId() const {
+ return raw->dlc_base_title_id;
+}
+
std::string NACP::GetVersionString() const {
- return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10);
+ return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
+ raw->version_string.size());
}
} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
std::string GetApplicationName(Language language = Language::Default) const;
std::string GetDeveloperName(Language language = Language::Default) const;
u64 GetTitleId() const;
+ u64 GetDLCBaseTitleId() const;
std::string GetVersionString() const;
private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 8c73a8f41..0117cb0bf 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -352,7 +352,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
- const auto& installed{Service::FileSystem::GetUnionContents()};
+ const auto installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e9b040689..1febb398e 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -308,14 +308,14 @@ VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
-std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
+std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
- return std::make_shared<NCA>(raw);
+ return std::make_unique<NCA>(raw);
}
-std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
+std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
@@ -516,7 +516,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
}) != yuzu_meta.end();
}
-RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches)
+RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
: caches(std::move(caches)) {}
void RegisteredCacheUnion::Refresh() {
@@ -572,14 +572,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
return GetEntryRaw(entry.title_id, entry.type);
}
-std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
+std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
- return std::make_shared<NCA>(raw);
+ return std::make_unique<NCA>(raw);
}
-std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
+std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index c0cd59fc5..5ddacba47 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,8 +88,8 @@ public:
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
- std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
- std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
+ std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
+ std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not boost::none, it will be filtered for from all entries.
@@ -142,7 +142,7 @@ private:
// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
class RegisteredCacheUnion {
public:
- explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches);
+ explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
void Refresh();
@@ -157,8 +157,8 @@ public:
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
- std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
- std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
+ std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
+ std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not boost::none, it will be filtered for from all entries.
@@ -168,7 +168,7 @@ public:
boost::optional<u64> title_id = boost::none) const;
private:
- std::vector<std::shared_ptr<RegisteredCache>> caches;
+ std::vector<RegisteredCache*> caches;
};
} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d66a9c9a4..bd3a57058 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -10,10 +10,10 @@
namespace FileSys {
SDMCFactory::SDMCFactory(VirtualDir dir_)
- : dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>(
+ : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
[](const VirtualFile& file, const NcaID& id) {
- return std::make_shared<NAX>(file, id)->GetDecrypted();
+ return NAX{file, id}.GetDecrypted();
})) {}
SDMCFactory::~SDMCFactory() = default;
@@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
return MakeResult<VirtualDir>(dir);
}
-std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const {
- return contents;
+RegisteredCache* SDMCFactory::GetSDMCContents() const {
+ return contents.get();
}
} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index ea12149de..42794ba5b 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,12 +19,12 @@ public:
~SDMCFactory();
ResultVal<VirtualDir> Open();
- std::shared_ptr<RegisteredCache> GetSDMCContents() const;
+ RegisteredCache* GetSDMCContents() const;
private:
VirtualDir dir;
- std::shared_ptr<RegisteredCache> contents;
+ std::unique_ptr<RegisteredCache> contents;
};
} // namespace FileSys
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 270291631..7f0d520ca 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -12,20 +12,12 @@
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
namespace FileSys {
-class VfsDirectory;
-class VfsFile;
-class VfsFilesystem;
-
enum class Mode : u32;
-// Convenience typedefs to use Vfs* interfaces
-using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
-using VirtualDir = std::shared_ptr<VfsDirectory>;
-using VirtualFile = std::shared_ptr<VfsFile>;
-
// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
None,
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h
new file mode 100644
index 000000000..6215ed7af
--- /dev/null
+++ b/src/core/file_sys/vfs_types.h
@@ -0,0 +1,21 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+namespace FileSys {
+
+class VfsDirectory;
+class VfsFile;
+class VfsFilesystem;
+
+// Declarations for Vfs* pointer types
+
+using VirtualDir = std::shared_ptr<VfsDirectory>;
+using VirtualFile = std::shared_ptr<VfsFile>;
+using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
+
+} // namespace FileSys
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index e961ef121..bdcc889e0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -207,7 +207,7 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
static Kernel::Thread* FindThreadById(int id) {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
- const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
+ const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadID() == static_cast<u32>(id)) {
current_core = core;
@@ -597,7 +597,7 @@ static void HandleQuery() {
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
- const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
+ const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x}", thread->GetThreadID());
val += ",";
@@ -612,7 +612,7 @@ static void HandleQuery() {
buffer += "l<?xml version=\"1.0\"?>";
buffer += "<threads>";
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
- const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
+ const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index ebf193930..57157beb4 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -39,7 +39,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
- const auto& thread_list = scheduler->GetThreadList();
+ const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetArbiterWaitAddress() == arb_addr)
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index d51562d92..d87a62bb9 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
- case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 9eb72315c..c9f4d0bb3 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -26,7 +26,6 @@ enum class HandleType : u32 {
AddressArbiter,
Timer,
ResourceLimit,
- CodeSet,
ClientPort,
ServerPort,
ClientSession,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index fb0027a71..073dd5a7d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,13 +20,7 @@
namespace Kernel {
-SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) {
- SharedPtr<CodeSet> codeset(new CodeSet(kernel));
- codeset->name = std::move(name);
- return codeset;
-}
-
-CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
+CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -159,11 +153,11 @@ void Process::PrepareForTermination() {
}
};
- auto& system = Core::System::GetInstance();
- stop_threads(system.Scheduler(0)->GetThreadList());
- stop_threads(system.Scheduler(1)->GetThreadList());
- stop_threads(system.Scheduler(2)->GetThreadList());
- stop_threads(system.Scheduler(3)->GetThreadList());
+ const auto& system = Core::System::GetInstance();
+ stop_threads(system.Scheduler(0).GetThreadList());
+ stop_threads(system.Scheduler(1).GetThreadList());
+ stop_threads(system.Scheduler(2).GetThreadList());
+ stop_threads(system.Scheduler(3).GetThreadList());
}
/**
@@ -224,20 +218,20 @@ void Process::FreeTLSSlot(VAddr tls_address) {
tls_slots[tls_page].reset(tls_slot);
}
-void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
+void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
- auto vma = vm_manager
- .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
- segment.size, memory_state)
- .Unwrap();
+ const auto vma = vm_manager
+ .MapMemoryBlock(segment.addr + base_addr, module_.memory,
+ segment.offset, segment.size, memory_state)
+ .Unwrap();
vm_manager.Reprotect(vma, permissions);
};
// Map CodeSet segments
- MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
- MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
- MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
+ MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
+ MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
+ MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 590e0c73d..f2816943a 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -24,6 +24,7 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
+class ResourceLimit;
struct AddressMapping {
// Address and size must be page-aligned
@@ -57,30 +58,33 @@ union ProcessFlags {
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
};
-enum class ProcessStatus { Created, Running, Exited };
-
-class ResourceLimit;
+/**
+ * Indicates the status of a Process instance.
+ *
+ * @note These match the values as used by kernel,
+ * so new entries should only be added if RE
+ * shows that a new value has been introduced.
+ */
+enum class ProcessStatus {
+ Created,
+ CreatedWithDebuggerAttached,
+ Running,
+ WaitingForDebuggerToAttach,
+ DebuggerAttached,
+ Exiting,
+ Exited,
+ DebugBreak,
+};
-struct CodeSet final : public Object {
+struct CodeSet final {
struct Segment {
std::size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
- static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name);
-
- std::string GetTypeName() const override {
- return "CodeSet";
- }
- std::string GetName() const override {
- return name;
- }
-
- static const HandleType HANDLE_TYPE = HandleType::CodeSet;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
+ explicit CodeSet();
+ ~CodeSet();
Segment& CodeSegment() {
return segments[0];
@@ -109,14 +113,7 @@ struct CodeSet final : public Object {
std::shared_ptr<std::vector<u8>> memory;
std::array<Segment, 3> segments;
- VAddr entrypoint;
-
- /// Name of the process
- std::string name;
-
-private:
- explicit CodeSet(KernelCore& kernel);
- ~CodeSet() override;
+ VAddr entrypoint = 0;
};
class Process final : public Object {
@@ -219,7 +216,7 @@ public:
*/
void PrepareForTermination();
- void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
+ void LoadModule(CodeSet module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 863ecfa74..3e5f11f2b 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -95,12 +95,12 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
const VAddr dst_end_address = dst_addr + size;
if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
- dst_addr < vm_manager.GetHeapRegionEndAddress()) {
+ vm_manager.GetHeapRegionEndAddress() > dst_addr) {
return ERR_INVALID_MEMORY_RANGE;
}
- if (dst_end_address > vm_manager.GetNewMapRegionBaseAddress() &&
- dst_addr < vm_manager.GetMapRegionEndAddress()) {
+ if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
+ vm_manager.GetMapRegionEndAddress() > dst_addr) {
return ERR_INVALID_MEMORY_RANGE;
}
@@ -389,6 +389,12 @@ static void Break(u32 reason, u64 info1, u64 info2) {
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
ASSERT(false);
+
+ Core::CurrentProcess()->PrepareForTermination();
+
+ // Kill the current thread
+ GetCurrentThread()->Stop();
+ Core::System::GetInstance().PrepareReschedule();
}
}
@@ -803,7 +809,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr condvar_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
- const auto& thread_list = scheduler->GetThreadList();
+ const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetCondVarWaitAddress() == condvar_addr)
@@ -1092,6 +1098,29 @@ static ResultCode ClearEvent(Handle handle) {
return RESULT_SUCCESS;
}
+static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
+
+ // This function currently only allows retrieving a process' status.
+ enum class InfoType {
+ Status,
+ };
+
+ const auto& kernel = Core::System::GetInstance().Kernel();
+ const auto process = kernel.HandleTable().Get<Process>(process_handle);
+ if (!process) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ const auto info_type = static_cast<InfoType>(type);
+ if (info_type != InfoType::Status) {
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ *out = static_cast<u64>(process->GetStatus());
+ return RESULT_SUCCESS;
+}
+
namespace {
struct FunctionDef {
using Func = void();
@@ -1227,7 +1256,7 @@ static const FunctionDef SVC_Table[] = {
{0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
- {0x7C, nullptr, "GetProcessInfo"},
+ {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
{0x7D, nullptr, "CreateResourceLimit"},
{0x7E, nullptr, "SetResourceLimitLimitValue"},
{0x7F, nullptr, "CallSecureMonitor"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index cbb80c3c4..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -77,6 +77,14 @@ void SvcWrap() {
FuncReturn(retval);
}
+template <ResultCode func(u64*, u32, u32)>
+void SvcWrap() {
+ u64 param_1 = 0;
+ u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
+ Core::CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 352ce1725..35ec98c1a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -97,7 +97,7 @@ void Thread::CancelWakeupTimer() {
static boost::optional<s32> GetNextProcessorId(u64 mask) {
for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
if (mask & (1ULL << index)) {
- if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) {
+ if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
// Core is enabled and not running any threads, use this one
return index;
}
@@ -147,14 +147,14 @@ void Thread::ResumeFromWait() {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
- Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
+ Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
- auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
+ auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
@@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
- scheduler = next_scheduler.get();
+ scheduler = next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
@@ -230,7 +230,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->name = std::move(name);
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = &owner_process;
- thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get();
+ thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
@@ -375,14 +375,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
- Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
+ Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
- auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
+ auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
@@ -397,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
- scheduler = next_scheduler.get();
+ scheduler = next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0ecfb5af1..518161bf7 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,8 +7,10 @@
#include <vector>
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
+#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
+#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
@@ -19,7 +21,7 @@
namespace Service::AOC {
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
-constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000;
+constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
@@ -97,14 +99,24 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(out);
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
+ rb.Push(count);
}
void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK);
+ const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+ FileSys::PatchManager pm{title_id};
+
+ const auto res = pm.GetControlMetadata();
+ if (res.first == nullptr) {
+ rb.Push(title_id + DLC_BASE_TO_AOC_ID);
+ return;
+ }
+
+ rb.Push(res.first->GetDLCBaseTitleId());
}
void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 439e62d27..e32a7c48e 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -319,13 +319,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
return sdmc_factory->Open();
}
-std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
- return std::make_shared<FileSys::RegisteredCacheUnion>(
- std::vector<std::shared_ptr<FileSys::RegisteredCache>>{
- GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
+std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
+ return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
+ GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
}
-std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
+FileSys::RegisteredCache* GetSystemNANDContents() {
LOG_TRACE(Service_FS, "Opening System NAND Contents");
if (bis_factory == nullptr)
@@ -334,7 +333,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
return bis_factory->GetSystemNANDContents();
}
-std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {
+FileSys::RegisteredCache* GetUserNANDContents() {
LOG_TRACE(Service_FS, "Opening User NAND Contents");
if (bis_factory == nullptr)
@@ -343,7 +342,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {
return bis_factory->GetUserNANDContents();
}
-std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() {
+FileSys::RegisteredCache* GetSDMCContents() {
LOG_TRACE(Service_FS, "Opening SDMC Contents");
if (sdmc_factory == nullptr)
@@ -361,19 +360,19 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
return bis_factory->GetModificationLoadRoot(title_id);
}
-void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
+void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr;
}
- auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
- FileSys::Mode::ReadWrite);
- auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
- FileSys::Mode::ReadWrite);
- auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
- FileSys::Mode::ReadWrite);
+ auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
+ FileSys::Mode::ReadWrite);
+ auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
+ FileSys::Mode::ReadWrite);
+ auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
+ FileSys::Mode::ReadWrite);
if (bis_factory == nullptr)
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
@@ -383,7 +382,7 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
}
-void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) {
+void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
romfs_factory = nullptr;
CreateFactories(vfs, false);
std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 53b01bb01..6ca5c5636 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -47,19 +47,19 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct);
ResultVal<FileSys::VirtualDir> OpenSDMC();
-std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
+std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
-std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
-std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
-std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
+FileSys::RegisteredCache* GetSystemNANDContents();
+FileSys::RegisteredCache* GetUserNANDContents();
+FileSys::RegisteredCache* GetSDMCContents();
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
-void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true);
+void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
-void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs);
+void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs);
// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 4b2f758a8..44accecb7 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -161,7 +161,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
};
RegisterHandlers(functions);
// Attempt to load shared font data from disk
- const auto nand = FileSystem::GetSystemNANDContents();
+ const auto* nand = FileSystem::GetSystemNANDContents();
std::size_t offset = 0;
// Rebuild shared fonts from data ncas
if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 62f049660..a225cb4cb 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -197,7 +197,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
// Module interface
/// Initialize ServiceManager
-void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
+void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
// here and pass it into the respective InstallInterfaces functions.
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -220,7 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
EUPLD::InstallInterfaces(*sm);
Fatal::InstallInterfaces(*sm);
FGM::InstallInterfaces(*sm);
- FileSystem::InstallInterfaces(*sm, rfs);
+ FileSystem::InstallInterfaces(*sm, vfs);
Friend::InstallInterfaces(*sm);
GRC::InstallInterfaces(*sm);
HID::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2fc57a82e..98483ecf1 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -180,8 +180,7 @@ private:
};
/// Initialize ServiceManager
-void Init(std::shared_ptr<SM::ServiceManager>& sm,
- const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
+void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);
/// Shutdown ServiceManager
void Shutdown();
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index bbc02abcc..184537daa 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -968,6 +968,54 @@ private:
rb.PushCopyObjects(vsync_event);
}
+ enum class ConvertedScaleMode : u64 {
+ None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no
+ // scaling/default
+ Freeze = 1,
+ ScaleToWindow = 2,
+ Crop = 3,
+ NoCrop = 4,
+ };
+
+ // This struct is different, currently it's 1:1 but this might change in the future.
+ enum class NintendoScaleMode : u32 {
+ None = 0,
+ Freeze = 1,
+ ScaleToWindow = 2,
+ Crop = 3,
+ NoCrop = 4,
+ };
+
+ void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto mode = rp.PopEnum<NintendoScaleMode>();
+ LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode));
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ switch (mode) {
+ case NintendoScaleMode::None:
+ rb.PushEnum(ConvertedScaleMode::None);
+ break;
+ case NintendoScaleMode::Freeze:
+ rb.PushEnum(ConvertedScaleMode::Freeze);
+ break;
+ case NintendoScaleMode::ScaleToWindow:
+ rb.PushEnum(ConvertedScaleMode::ScaleToWindow);
+ break;
+ case NintendoScaleMode::Crop:
+ rb.PushEnum(ConvertedScaleMode::Crop);
+ break;
+ case NintendoScaleMode::NoCrop:
+ rb.PushEnum(ConvertedScaleMode::NoCrop);
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode));
+ rb.PushEnum(ConvertedScaleMode::None);
+ break;
+ }
+ }
+
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
@@ -991,7 +1039,7 @@ IApplicationDisplayService::IApplicationDisplayService(
{2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
{2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
{2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
- {2102, nullptr, "ConvertScalingMode"},
+ {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
{2450, nullptr, "GetIndirectLayerImageMap"},
{2451, nullptr, "GetIndirectLayerImageCropMap"},
{2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"},
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 951fd8257..8518dddcb 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -139,14 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const FileSys::VirtualFile module_file = dir->GetFile(module);
- if (module_file != nullptr) {
- const VAddr load_addr = next_load_addr;
- next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr,
- std::strcmp(module, "rtld") == 0, pm);
- LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
- // Register module with GDBStub
- GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
+ if (module_file == nullptr) {
+ continue;
}
+
+ const VAddr load_addr = next_load_addr;
+ const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
+ const auto tentative_next_load_addr =
+ AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
+ if (!tentative_next_load_addr) {
+ return ResultStatus::ErrorLoadingNSO;
+ }
+
+ next_load_addr = *tentative_next_load_addr;
+ LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
+ // Register module with GDBStub
+ GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
}
process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index e67b49fc9..6057c7f26 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,16 +9,11 @@
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
-#include "core/core.h"
-#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/elf.h"
#include "core/memory.h"
-using Kernel::CodeSet;
-using Kernel::SharedPtr;
-
////////////////////////////////////////////////////////////////////////////////////////////////////
// ELF Header Constants
@@ -211,7 +206,7 @@ public:
u32 GetFlags() const {
return (u32)(header->e_flags);
}
- SharedPtr<CodeSet> LoadInto(VAddr vaddr);
+ Kernel::CodeSet LoadInto(VAddr vaddr);
int GetNumSegments() const {
return (int)(header->e_phnum);
@@ -274,7 +269,7 @@ const char* ElfReader::GetSectionName(int section) const {
return nullptr;
}
-SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
+Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
// Should we relocate?
@@ -302,8 +297,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
std::vector<u8> program_image(total_image_size);
std::size_t current_image_position = 0;
- auto& kernel = Core::System::GetInstance().Kernel();
- SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
+ Kernel::CodeSet codeset;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
@@ -311,14 +305,14 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
p->p_vaddr, p->p_filesz, p->p_memsz);
if (p->p_type == PT_LOAD) {
- CodeSet::Segment* codeset_segment;
+ Kernel::CodeSet::Segment* codeset_segment;
u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
if (permission_flags == (PF_R | PF_X)) {
- codeset_segment = &codeset->CodeSegment();
+ codeset_segment = &codeset.CodeSegment();
} else if (permission_flags == (PF_R)) {
- codeset_segment = &codeset->RODataSegment();
+ codeset_segment = &codeset.RODataSegment();
} else if (permission_flags == (PF_R | PF_W)) {
- codeset_segment = &codeset->DataSegment();
+ codeset_segment = &codeset.DataSegment();
} else {
LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
p->p_flags);
@@ -345,8 +339,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
}
}
- codeset->entrypoint = base_addr + header->e_entry;
- codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ codeset.entrypoint = base_addr + header->e_entry;
+ codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
LOG_DEBUG(Loader, "Done loading.");
@@ -397,11 +391,11 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
ElfReader elf_reader(&buffer[0]);
- SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address);
- codeset->name = file->GetName();
+ Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
+ const VAddr entry_point = codeset.entrypoint;
- process.LoadModule(codeset, codeset->entrypoint);
- process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE);
+ process.LoadModule(std::move(codeset), entry_point);
+ process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 91659ec17..9cd0b0ccd 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown";
}
-constexpr std::array<const char*, 59> RESULT_MESSAGES{
+constexpr std::array<const char*, 60> RESULT_MESSAGES{
"The operation completed successfully.",
"The loader requested to load is already loaded.",
"The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
"The RomFS could not be found.",
"The ELF file has incorrect size as determined by the header.",
"There was a general error loading the NRO into emulated memory.",
+ "There was a general error loading the NSO into emulated memory.",
"There is no icon available.",
"There is no control data available.",
"The NAX file has a bad header.",
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 0e0333db5..e562b3a04 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -90,6 +90,7 @@ enum class ResultStatus : u16 {
ErrorNoRomFS,
ErrorIncorrectELFFileSize,
ErrorLoadingNRO,
+ ErrorLoadingNSO,
ErrorNoIcon,
ErrorNoControl,
ErrorBadNAXHeader,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 25dd3f04e..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,7 +14,6 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
-#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nro.h"
@@ -128,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
-bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
+bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
// Read NSO header
NroHeader nro_header{};
- if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
+ if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
return {};
}
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -139,22 +138,21 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
}
// Build program image
- auto& kernel = Core::System::GetInstance().Kernel();
- Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
- std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
+ std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
return {};
}
+ Kernel::CodeSet codeset;
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
- codeset->segments[i].addr = nro_header.segments[i].offset;
- codeset->segments[i].offset = nro_header.segments[i].offset;
- codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size);
+ codeset.segments[i].addr = nro_header.segments[i].offset;
+ codeset.segments[i].offset = nro_header.segments[i].offset;
+ codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
}
if (!Settings::values.program_args.empty()) {
const auto arg_data = Settings::values.program_args;
- codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
+ codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
const auto end_offset = program_image.size();
@@ -176,16 +174,15 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
- codeset->DataSegment().size += bss_size;
+ codeset.DataSegment().size += bss_size;
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
- codeset->name = file->GetName();
- codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
- Core::CurrentProcess()->LoadModule(codeset, load_base);
+ codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
- GDBStub::RegisterModule(codeset->name, load_base, load_base);
+ GDBStub::RegisterModule(file.GetName(), load_base, load_base);
return true;
}
@@ -198,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
// Load NRO
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
- if (!LoadNro(file, base_address)) {
+ if (!LoadNro(*file, base_address)) {
return ResultStatus::ErrorLoadingNRO;
}
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04b46119a..50ee5a78a 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -41,7 +41,7 @@ public:
bool IsRomFSUpdatable() const override;
private:
- bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
+ bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
std::vector<u8> icon_data;
std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 28c6dd9b7..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -12,7 +12,6 @@
#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/gdbstub/gdbstub.h"
-#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nso.h"
@@ -94,42 +93,38 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
-VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
- bool should_pass_arguments,
- boost::optional<FileSys::PatchManager> pm) {
- if (file == nullptr)
- return {};
-
- if (file->GetSize() < sizeof(NsoHeader))
+std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
+ bool should_pass_arguments,
+ std::optional<FileSys::PatchManager> pm) {
+ if (file.GetSize() < sizeof(NsoHeader))
return {};
NsoHeader nso_header{};
- if (sizeof(NsoHeader) != file->ReadObject(&nso_header))
+ if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
return {};
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return {};
// Build program image
- auto& kernel = Core::System::GetInstance().Kernel();
- Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
+ Kernel::CodeSet codeset;
std::vector<u8> program_image;
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> data =
- file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
+ file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
if (nso_header.IsSegmentCompressed(i)) {
data = DecompressSegment(data, nso_header.segments[i]);
}
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
- codeset->segments[i].addr = nso_header.segments[i].location;
- codeset->segments[i].offset = nso_header.segments[i].location;
- codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
+ codeset.segments[i].addr = nso_header.segments[i].location;
+ codeset.segments[i].offset = nso_header.segments[i].location;
+ codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
}
if (should_pass_arguments && !Settings::values.program_args.empty()) {
const auto arg_data = Settings::values.program_args;
- codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
+ codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
const auto end_offset = program_image.size();
@@ -154,12 +149,12 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
- codeset->DataSegment().size += bss_size;
+ codeset.DataSegment().size += bss_size;
const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
program_image.resize(image_size);
// Apply patches if necessary
- if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) {
+ if (pm && pm->HasNSOPatch(nso_header.build_id)) {
std::vector<u8> pi_header(program_image.size() + 0x100);
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
@@ -170,12 +165,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
}
// Load codeset for current process
- codeset->name = file->GetName();
- codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
- Core::CurrentProcess()->LoadModule(codeset, load_base);
+ codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
- GDBStub::RegisterModule(codeset->name, load_base, load_base);
+ GDBStub::RegisterModule(file.GetName(), load_base, load_base);
return load_base + image_size;
}
@@ -187,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
// Load module
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
- LoadModule(file, base_address, true);
+ if (!LoadModule(*file, base_address, true)) {
+ return ResultStatus::ErrorLoadingNSO;
+ }
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 70ab3b718..433306139 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,6 +4,7 @@
#pragma once
+#include <optional>
#include "common/common_types.h"
#include "core/file_sys/patch_manager.h"
#include "core/loader/linker.h"
@@ -36,8 +37,9 @@ public:
return IdentifyType(file);
}
- static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments,
- boost::optional<FileSys::PatchManager> pm = boost::none);
+ static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
+ bool should_pass_arguments,
+ std::optional<FileSys::PatchManager> pm = {});
ResultStatus Load(Kernel::Process& process) override;
};
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 912e785b9..597b279b9 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -62,14 +62,16 @@ void Fermi2D::HandleSurfaceCopy() {
u8* dst_buffer = Memory::GetPointer(dest_cpu);
if (!regs.src.linear && regs.dst.linear) {
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
- Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
- dst_bytes_per_pixel, src_buffer, dst_buffer, true,
- regs.src.BlockHeight());
+ Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
+ src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
+ dst_buffer, true, regs.src.BlockHeight(),
+ regs.src.BlockDepth());
} else {
// If the input is linear and the output is tiled, swizzle the input and copy it over.
- Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
- dst_bytes_per_pixel, dst_buffer, src_buffer, false,
- regs.dst.BlockHeight());
+ Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
+ src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
+ src_buffer, false, regs.dst.BlockHeight(),
+ regs.dst.BlockDepth());
}
}
}
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index aa7481b8c..bf2a21bb6 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -68,12 +68,14 @@ void MaxwellDMA::HandleCopy() {
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
- Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer,
- dst_buffer, true, regs.src_params.BlockHeight());
+ Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y,
+ regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true,
+ regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
} else {
// If the input is linear and the output is tiled, swizzle the input and copy it over.
- Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer,
- src_buffer, false, regs.dst_params.BlockHeight());
+ Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y,
+ regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false,
+ regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
}
}
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 311ccb616..df19e02e2 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -43,6 +43,10 @@ public:
u32 BlockHeight() const {
return 1 << block_height;
}
+
+ u32 BlockDepth() const {
+ return 1 << block_depth;
+ }
};
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 9a59b65b3..f356f9a03 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -267,7 +267,7 @@ enum class ControlCode : u64 {
GTU = 12,
NEU = 13,
GEU = 14,
- //
+ T = 15,
OFF = 16,
LO = 17,
SFF = 18,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84582c777..8d5f277e2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -286,7 +286,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
&ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
// Bind the buffer
- glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo));
+ glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
+ offset, static_cast<GLsizeiptr>(sizeof(ubo)));
Shader shader{shader_cache.GetStageProgram(program)};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 65a220c41..801d45144 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -231,6 +231,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
{GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
+ {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
+ {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
// Depth formats
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -277,7 +279,9 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
static bool IsPixelFormatASTC(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
+ case PixelFormat::ASTC_2D_5X4:
case PixelFormat::ASTC_2D_8X8:
+ case PixelFormat::ASTC_2D_8X5:
return true;
default:
return false;
@@ -288,8 +292,12 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
return {4, 4};
+ case PixelFormat::ASTC_2D_5X4:
+ return {5, 4};
case PixelFormat::ASTC_2D_8X8:
return {8, 8};
+ case PixelFormat::ASTC_2D_8X5:
+ return {8, 5};
default:
LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
UNREACHABLE();
@@ -323,8 +331,8 @@ static bool IsFormatBCn(PixelFormat format) {
}
template <bool morton_to_gl, PixelFormat format>
-void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size,
- VAddr addr) {
+void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
+ std::size_t gl_buffer_size, VAddr addr) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
@@ -333,7 +341,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::si
// pixel values.
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
- addr, tile_size, bytes_per_pixel, stride, height, block_height);
+ addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
memcpy(gl_buffer, data.data(), size_to_copy);
} else {
@@ -345,7 +353,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::si
}
}
-static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
+static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
SurfaceParams::MaxPixelFormat>
morton_to_gl_fns = {
// clang-format off
@@ -395,6 +403,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
MortonCopy<true, PixelFormat::RG32UI>,
MortonCopy<true, PixelFormat::R32UI>,
MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
+ MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
+ MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::Z16>,
MortonCopy<true, PixelFormat::Z24S8>,
@@ -403,7 +413,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
// clang-format on
};
-static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
+static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
SurfaceParams::MaxPixelFormat>
gl_to_morton_fns = {
// clang-format off
@@ -455,6 +465,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
MortonCopy<false, PixelFormat::RG32UI>,
MortonCopy<false, PixelFormat::R32UI>,
nullptr,
+ nullptr,
+ nullptr,
MortonCopy<false, PixelFormat::Z32F>,
MortonCopy<false, PixelFormat::Z16>,
MortonCopy<false, PixelFormat::Z24S8>,
@@ -790,7 +802,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
u32 width, u32 height) {
switch (pixel_format) {
case PixelFormat::ASTC_2D_4X4:
- case PixelFormat::ASTC_2D_8X8: {
+ case PixelFormat::ASTC_2D_8X8:
+ case PixelFormat::ASTC_2D_8X5:
+ case PixelFormat::ASTC_2D_5X4: {
// Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
u32 block_width{};
u32 block_height{};
@@ -827,36 +841,23 @@ void CachedSurface::LoadGLBuffer() {
if (params.is_tiled) {
gl_buffer.resize(total_size);
+ u32 depth = params.depth;
+ u32 block_depth = params.block_depth;
ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
params.block_width, static_cast<u32>(params.target));
- ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}",
- params.block_depth, static_cast<u32>(params.target));
- // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do
- // this for 3D textures, etc.
- switch (params.target) {
- case SurfaceParams::SurfaceTarget::Texture2D:
- // Pass impl. to the fallback code below
- break;
- case SurfaceParams::SurfaceTarget::Texture2DArray:
- case SurfaceParams::SurfaceTarget::TextureCubemap:
- for (std::size_t index = 0; index < params.depth; ++index) {
- const std::size_t offset{index * copy_size};
- morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
- params.width, params.block_height, params.height, gl_buffer.data() + offset,
- copy_size, params.addr + offset);
- }
- break;
- default:
- LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
- static_cast<u32>(params.target));
- UNREACHABLE();
+ if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
+ // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
+ depth = 1U;
+ block_depth = 1U;
}
+ const std::size_t size = copy_size * depth;
+
morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
- params.width, params.block_height, params.height, gl_buffer.data(), copy_size,
- params.addr);
+ params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
+ size, params.addr);
} else {
const u8* const texture_src_data_end{texture_src_data + total_size};
gl_buffer.assign(texture_src_data, texture_src_data_end);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 66d98ad4e..0b8ae3eb4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -74,19 +74,21 @@ struct SurfaceParams {
RG32UI = 43,
R32UI = 44,
ASTC_2D_8X8 = 45,
+ ASTC_2D_8X5 = 46,
+ ASTC_2D_5X4 = 47,
MaxColorFormat,
// Depth formats
- Z32F = 46,
- Z16 = 47,
+ Z32F = 48,
+ Z16 = 49,
MaxDepthFormat,
// DepthStencil formats
- Z24S8 = 48,
- S8Z24 = 49,
- Z32FS8 = 50,
+ Z24S8 = 50,
+ S8Z24 = 51,
+ Z32FS8 = 52,
MaxDepthStencilFormat,
@@ -220,6 +222,8 @@ struct SurfaceParams {
1, // RG32UI
1, // R32UI
4, // ASTC_2D_8X8
+ 4, // ASTC_2D_8X5
+ 4, // ASTC_2D_5X4
1, // Z32F
1, // Z16
1, // Z24S8
@@ -282,6 +286,8 @@ struct SurfaceParams {
64, // RG32UI
32, // R32UI
16, // ASTC_2D_8X8
+ 32, // ASTC_2D_8X5
+ 32, // ASTC_2D_5X4
32, // Z32F
16, // Z16
32, // Z24S8
@@ -553,8 +559,12 @@ struct SurfaceParams {
return PixelFormat::BC6H_SF16;
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
return PixelFormat::ASTC_2D_4X4;
+ case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
+ return PixelFormat::ASTC_2D_5X4;
case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
return PixelFormat::ASTC_2D_8X8;
+ case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
+ return PixelFormat::ASTC_2D_8X5;
case Tegra::Texture::TextureFormat::R16_G16:
switch (component_type) {
case Tegra::Texture::ComponentType::FLOAT:
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 8dfb49507..ca063d90d 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1436,7 +1436,6 @@ private:
break;
}
-
case OpCode::Type::Shift: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
std::string op_b;
@@ -1478,7 +1477,6 @@ private:
}
break;
}
-
case OpCode::Type::ArithmeticIntegerImmediate: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
std::string op_b = std::to_string(instr.alu.imm20_32.Value());
@@ -2626,14 +2624,14 @@ private:
const std::string pred =
GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
const std::string combiner = GetPredicateCombiner(instr.csetp.op);
- const std::string controlCode = regs.GetControlCode(instr.csetp.cc);
+ const std::string control_code = regs.GetControlCode(instr.csetp.cc);
if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
SetPredicate(instr.csetp.pred3,
- '(' + controlCode + ") " + combiner + " (" + pred + ')');
+ '(' + control_code + ") " + combiner + " (" + pred + ')');
}
if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
SetPredicate(instr.csetp.pred0,
- "!(" + controlCode + ") " + combiner + " (" + pred + ')');
+ "!(" + control_code + ") " + combiner + " (" + pred + ')');
}
break;
}
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 3d5476e5d..18ab723f7 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -4,6 +4,7 @@
#include <cmath>
#include <cstring>
+#include "common/alignment.h"
#include "common/assert.h"
#include "core/memory.h"
#include "video_core/gpu.h"
@@ -39,72 +40,146 @@ struct alignas(64) SwizzleTable {
constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
-static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
- u8* swizzled_data, u8* unswizzled_data, bool unswizzle,
- u32 block_height) {
+/**
+ * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
+ * Instead of going gob by gob, we map the coordinates inside a block and manage from
+ * those. Block_Width is assumed to be 1.
+ */
+void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
+ const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
+ const u32 y_end, const u32 z_end, const u32 tile_offset,
+ const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
+ const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
std::array<u8*, 2> data_ptrs;
- const std::size_t stride = width * bytes_per_pixel;
- const std::size_t gobs_in_x = 64;
- const std::size_t gobs_in_y = 8;
- const std::size_t gobs_size = gobs_in_x * gobs_in_y;
- const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x};
- for (std::size_t y = 0; y < height; ++y) {
- const std::size_t gob_y_address =
- (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs +
- (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size;
- const auto& table = legacy_swizzle_table[y % gobs_in_y];
- for (std::size_t x = 0; x < width; ++x) {
- const std::size_t gob_address =
- gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height;
- const std::size_t x2 = x * bytes_per_pixel;
- const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x];
- const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel;
-
- data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
- data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
-
- std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
+ u32 z_address = tile_offset;
+ const u32 gob_size_x = 64;
+ const u32 gob_size_y = 8;
+ const u32 gob_size_z = 1;
+ const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
+ for (u32 z = z_start; z < z_end; z++) {
+ u32 y_address = z_address;
+ u32 pixel_base = layer_z * z + y_start * stride_x;
+ for (u32 y = y_start; y < y_end; y++) {
+ const auto& table = legacy_swizzle_table[y % gob_size_y];
+ for (u32 x = x_start; x < x_end; x++) {
+ const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
+ const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
+ data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
+ data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
+ std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
+ }
+ pixel_base += stride_x;
+ if ((y + 1) % gob_size_y == 0)
+ y_address += gob_size;
}
+ z_address += xy_block_size;
}
}
-static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
- u8* swizzled_data, u8* unswizzled_data, bool unswizzle,
- u32 block_height) {
+/**
+ * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
+ * Instead of going gob by gob, we map the coordinates inside a block and manage from
+ * those. Block_Width is assumed to be 1.
+ */
+void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
+ const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
+ const u32 y_end, const u32 z_end, const u32 tile_offset,
+ const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
+ const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
std::array<u8*, 2> data_ptrs;
- const std::size_t stride{width * bytes_per_pixel};
- const std::size_t gobs_in_x = 64;
- const std::size_t gobs_in_y = 8;
- const std::size_t gobs_size = gobs_in_x * gobs_in_y;
- const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x};
- const std::size_t copy_size{16};
- for (std::size_t y = 0; y < height; ++y) {
- const std::size_t initial_gob =
- (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs +
- (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size;
- const std::size_t pixel_base{y * width * out_bytes_per_pixel};
- const auto& table = fast_swizzle_table[y % gobs_in_y];
- for (std::size_t xb = 0; xb < stride; xb += copy_size) {
- const std::size_t gob_address{initial_gob +
- (xb / gobs_in_x) * gobs_size * block_height};
- const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]};
- const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
- const std::size_t pixel_index{out_x + pixel_base};
- data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
- data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
- std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
+ u32 z_address = tile_offset;
+ const u32 x_startb = x_start * bytes_per_pixel;
+ const u32 x_endb = x_end * bytes_per_pixel;
+ const u32 copy_size = 16;
+ const u32 gob_size_x = 64;
+ const u32 gob_size_y = 8;
+ const u32 gob_size_z = 1;
+ const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
+ for (u32 z = z_start; z < z_end; z++) {
+ u32 y_address = z_address;
+ u32 pixel_base = layer_z * z + y_start * stride_x;
+ for (u32 y = y_start; y < y_end; y++) {
+ const auto& table = fast_swizzle_table[y % gob_size_y];
+ for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
+ const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
+ const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
+ const u32 pixel_index{out_x + pixel_base};
+ data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
+ data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
+ std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
+ }
+ pixel_base += stride_x;
+ if ((y + 1) % gob_size_y == 0)
+ y_address += gob_size;
}
+ z_address += xy_block_size;
}
}
-void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
- u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) {
+/**
+ * This function unswizzles or swizzles a texture by mapping Linear to BlockLinear Textue.
+ * The body of this function takes care of splitting the swizzled texture into blocks,
+ * and managing the extents of it. Once all the parameters of a single block are obtained,
+ * the function calls 'ProcessBlock' to process that particular Block.
+ *
+ * Documentation for the memory layout and decoding can be found at:
+ * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
+ */
+template <bool fast>
+void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
+ const u32 height, const u32 depth, const u32 bytes_per_pixel,
+ const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
+ auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
+ const u32 stride_x = width * out_bytes_per_pixel;
+ const u32 layer_z = height * stride_x;
+ const u32 gob_x_bytes = 64;
+ const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
+ const u32 gob_elements_y = 8;
+ const u32 gob_elements_z = 1;
+ const u32 block_x_elements = gob_elements_x;
+ const u32 block_y_elements = gob_elements_y * block_height;
+ const u32 block_z_elements = gob_elements_z * block_depth;
+ const u32 blocks_on_x = div_ceil(width, block_x_elements);
+ const u32 blocks_on_y = div_ceil(height, block_y_elements);
+ const u32 blocks_on_z = div_ceil(depth, block_z_elements);
+ const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z;
+ const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
+ const u32 xy_block_size = gob_size * block_height;
+ const u32 block_size = xy_block_size * block_depth;
+ u32 tile_offset = 0;
+ for (u32 zb = 0; zb < blocks_on_z; zb++) {
+ const u32 z_start = zb * block_z_elements;
+ const u32 z_end = std::min(depth, z_start + block_z_elements);
+ for (u32 yb = 0; yb < blocks_on_y; yb++) {
+ const u32 y_start = yb * block_y_elements;
+ const u32 y_end = std::min(height, y_start + block_y_elements);
+ for (u32 xb = 0; xb < blocks_on_x; xb++) {
+ const u32 x_start = xb * block_x_elements;
+ const u32 x_end = std::min(width, x_start + block_x_elements);
+ if (fast) {
+ FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
+ z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
+ layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
+ } else {
+ PreciseProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
+ z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
+ layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
+ }
+ tile_offset += block_size;
+ }
+ }
+ }
+}
+
+void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
+ u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
+ bool unswizzle, u32 block_height, u32 block_depth) {
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
- FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data,
- unswizzled_data, unswizzle, block_height);
+ SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
+ bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
} else {
- LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data,
- unswizzled_data, unswizzle, block_height);
+ SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
+ bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
}
}
@@ -125,7 +200,9 @@ u32 BytesPerPixel(TextureFormat format) {
case TextureFormat::R32_G32_B32:
return 12;
case TextureFormat::ASTC_2D_4X4:
+ case TextureFormat::ASTC_2D_5X4:
case TextureFormat::ASTC_2D_8X8:
+ case TextureFormat::ASTC_2D_8X5:
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::BF10GF11RF11:
@@ -152,10 +229,11 @@ u32 BytesPerPixel(TextureFormat format) {
}
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
- u32 height, u32 block_height) {
- std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
- CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel,
- Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
+ u32 height, u32 depth, u32 block_height, u32 block_depth) {
+ std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
+ CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel,
+ Memory::GetPointer(address), unswizzled_data.data(), true, block_height,
+ block_depth);
return unswizzled_data;
}
@@ -199,4 +277,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
return rgba_data;
}
+std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
+ u32 block_height, u32 block_depth) {
+ if (tiled) {
+ const u32 gobs_in_x = 64 / bytes_per_pixel;
+ const u32 gobs_in_y = 8;
+ const u32 gobs_in_z = 1;
+ const u32 aligned_width = Common::AlignUp(width, gobs_in_x);
+ const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
+ const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
+ return aligned_width * aligned_height * aligned_depth * bytes_per_pixel;
+ } else {
+ return width * height * depth * bytes_per_pixel;
+ }
+}
+
} // namespace Tegra::Texture
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 1f7b731be..aaf316947 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -14,17 +14,14 @@ namespace Tegra::Texture {
* Unswizzles a swizzled texture without changing its format.
*/
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
- u32 height, u32 block_height = TICEntry::DefaultBlockHeight);
-
-/**
- * Unswizzles a swizzled depth texture without changing its format.
- */
-std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
- u32 block_height = TICEntry::DefaultBlockHeight);
+ u32 height, u32 depth,
+ u32 block_height = TICEntry::DefaultBlockHeight,
+ u32 block_depth = TICEntry::DefaultBlockHeight);
/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary.
-void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
- u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height);
+void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
+ u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
+ bool unswizzle, u32 block_height, u32 block_depth);
/**
* Decodes an unswizzled texture into a A8R8G8B8 texture.
@@ -32,4 +29,10 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
u32 height);
+/**
+ * This function calculates the correct size of a texture depending if it's tiled or not.
+ */
+std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
+ u32 block_height, u32 block_depth);
+
} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 58d17abcb..5947bd2b9 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -141,6 +141,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
struct TICEntry {
static constexpr u32 DefaultBlockHeight = 16;
+ static constexpr u32 DefaultBlockDepth = 1;
union {
u32 raw;
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 033ea1ea4..0a8f2bd9e 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -2,96 +2,114 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <thread>
-#include "common/assert.h"
+#include <json.hpp>
#include "common/detached_tasks.h"
+#include "common/web_result.h"
#include "web_service/telemetry_json.h"
#include "web_service/web_backend.h"
namespace WebService {
-TelemetryJson::TelemetryJson(const std::string& host, const std::string& username,
- const std::string& token)
- : host(std::move(host)), username(std::move(username)), token(std::move(token)) {}
-TelemetryJson::~TelemetryJson() = default;
+struct TelemetryJson::Impl {
+ Impl(std::string host, std::string username, std::string token)
+ : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
-template <class T>
-void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) {
- sections[static_cast<u8>(type)][name] = value;
-}
+ nlohmann::json& TopSection() {
+ return sections[static_cast<u8>(Telemetry::FieldType::None)];
+ }
-void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) {
- TopSection()[name] = sections[static_cast<unsigned>(type)];
-}
+ const nlohmann::json& TopSection() const {
+ return sections[static_cast<u8>(Telemetry::FieldType::None)];
+ }
+
+ template <class T>
+ void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
+ sections[static_cast<u8>(type)][name] = value;
+ }
+
+ void SerializeSection(Telemetry::FieldType type, const std::string& name) {
+ TopSection()[name] = sections[static_cast<unsigned>(type)];
+ }
+
+ nlohmann::json output;
+ std::array<nlohmann::json, 7> sections;
+ std::string host;
+ std::string username;
+ std::string token;
+};
+
+TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
+ : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
+TelemetryJson::~TelemetryJson() = default;
void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
- Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
+ impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
}
void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
- Serialize(field.GetType(), field.GetName(), field.GetValue().count());
+ impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
}
void TelemetryJson::Complete() {
- SerializeSection(Telemetry::FieldType::App, "App");
- SerializeSection(Telemetry::FieldType::Session, "Session");
- SerializeSection(Telemetry::FieldType::Performance, "Performance");
- SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
- SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
- SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
-
- auto content = TopSection().dump();
+ impl->SerializeSection(Telemetry::FieldType::App, "App");
+ impl->SerializeSection(Telemetry::FieldType::Session, "Session");
+ impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
+ impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
+ impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
+ impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
+
+ auto content = impl->TopSection().dump();
// Send the telemetry async but don't handle the errors since they were written to the log
Common::DetachedTasks::AddTask(
- [host{this->host}, username{this->username}, token{this->token}, content]() {
+ [host{impl->host}, username{impl->username}, token{impl->token}, content]() {
Client{host, username, token}.PostJson("/telemetry", content, true);
});
}
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 0fe6f9a3e..93371414a 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -4,11 +4,9 @@
#pragma once
-#include <array>
+#include <chrono>
#include <string>
-#include <json.hpp>
#include "common/telemetry.h"
-#include "common/web_result.h"
namespace WebService {
@@ -18,8 +16,8 @@ namespace WebService {
*/
class TelemetryJson : public Telemetry::VisitorInterface {
public:
- TelemetryJson(const std::string& host, const std::string& username, const std::string& token);
- ~TelemetryJson();
+ TelemetryJson(std::string host, std::string username, std::string token);
+ ~TelemetryJson() override;
void Visit(const Telemetry::Field<bool>& field) override;
void Visit(const Telemetry::Field<double>& field) override;
@@ -39,20 +37,8 @@ public:
void Complete() override;
private:
- nlohmann::json& TopSection() {
- return sections[static_cast<u8>(Telemetry::FieldType::None)];
- }
-
- template <class T>
- void Serialize(Telemetry::FieldType type, const std::string& name, T value);
-
- void SerializeSection(Telemetry::FieldType type, const std::string& name);
-
- nlohmann::json output;
- std::array<nlohmann::json, 7> sections;
- std::string host;
- std::string username;
- std::string token;
+ struct Impl;
+ std::unique_ptr<Impl> impl;
};
} // namespace WebService
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index 124aa3863..ca4b43b93 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <json.hpp>
+#include "common/web_result.h"
#include "web_service/verify_login.h"
#include "web_service/web_backend.h"
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 787b0fbcb..b7737b615 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -3,9 +3,11 @@
// Refer to the license.txt file included.
#include <cstdlib>
+#include <mutex>
#include <string>
-#include <thread>
#include <LUrlParser.h>
+#include <httplib.h>
+#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/web_result.h"
#include "core/settings.h"
@@ -20,99 +22,132 @@ constexpr u32 HTTPS_PORT = 443;
constexpr u32 TIMEOUT_SECONDS = 30;
-Client::JWTCache Client::jwt_cache{};
+struct Client::Impl {
+ Impl(std::string host, std::string username, std::string token)
+ : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
+ std::lock_guard<std::mutex> lock(jwt_cache.mutex);
+ if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
+ jwt = jwt_cache.jwt;
+ }
+ }
+
+ /// A generic function handles POST, GET and DELETE request together
+ Common::WebResult GenericJson(const std::string& method, const std::string& path,
+ const std::string& data, bool allow_anonymous) {
+ if (jwt.empty()) {
+ UpdateJWT();
+ }
+
+ if (jwt.empty() && !allow_anonymous) {
+ LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
+ return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
+ "Credentials needed"};
+ }
+
+ auto result = GenericJson(method, path, data, jwt);
+ if (result.result_string == "401") {
+ // Try again with new JWT
+ UpdateJWT();
+ result = GenericJson(method, path, data, jwt);
+ }
-Client::Client(const std::string& host, const std::string& username, const std::string& token)
- : host(host), username(username), token(token) {
- std::lock_guard<std::mutex> lock(jwt_cache.mutex);
- if (username == jwt_cache.username && token == jwt_cache.token) {
- jwt = jwt_cache.jwt;
+ return result;
}
-}
-Common::WebResult Client::GenericJson(const std::string& method, const std::string& path,
- const std::string& data, const std::string& jwt,
- const std::string& username, const std::string& token) {
- if (cli == nullptr) {
- auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
- int port;
- if (parsedUrl.m_Scheme == "http") {
- if (!parsedUrl.GetPort(&port)) {
- port = HTTP_PORT;
- }
- cli =
- std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS);
- } else if (parsedUrl.m_Scheme == "https") {
- if (!parsedUrl.GetPort(&port)) {
- port = HTTPS_PORT;
+ /**
+ * A generic function with explicit authentication method specified
+ * JWT is used if the jwt parameter is not empty
+ * username + token is used if jwt is empty but username and token are not empty
+ * anonymous if all of jwt, username and token are empty
+ */
+ Common::WebResult GenericJson(const std::string& method, const std::string& path,
+ const std::string& data, const std::string& jwt = "",
+ const std::string& username = "", const std::string& token = "") {
+ if (cli == nullptr) {
+ auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
+ int port;
+ if (parsedUrl.m_Scheme == "http") {
+ if (!parsedUrl.GetPort(&port)) {
+ port = HTTP_PORT;
+ }
+ cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port,
+ TIMEOUT_SECONDS);
+ } else if (parsedUrl.m_Scheme == "https") {
+ if (!parsedUrl.GetPort(&port)) {
+ port = HTTPS_PORT;
+ }
+ cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
+ TIMEOUT_SECONDS);
+ } else {
+ LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
+ return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
}
- cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
- TIMEOUT_SECONDS);
- } else {
- LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
- return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
}
- }
- if (cli == nullptr) {
- LOG_ERROR(WebService, "Invalid URL {}", host + path);
- return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
- }
+ if (cli == nullptr) {
+ LOG_ERROR(WebService, "Invalid URL {}", host + path);
+ return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
+ }
- httplib::Headers params;
- if (!jwt.empty()) {
- params = {
- {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
- };
- } else if (!username.empty()) {
- params = {
- {std::string("x-username"), username},
- {std::string("x-token"), token},
+ httplib::Headers params;
+ if (!jwt.empty()) {
+ params = {
+ {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
+ };
+ } else if (!username.empty()) {
+ params = {
+ {std::string("x-username"), username},
+ {std::string("x-token"), token},
+ };
+ }
+
+ params.emplace(std::string("api-version"),
+ std::string(API_VERSION.begin(), API_VERSION.end()));
+ if (method != "GET") {
+ params.emplace(std::string("Content-Type"), std::string("application/json"));
};
- }
- params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end()));
- if (method != "GET") {
- params.emplace(std::string("Content-Type"), std::string("application/json"));
- };
+ httplib::Request request;
+ request.method = method;
+ request.path = path;
+ request.headers = params;
+ request.body = data;
- httplib::Request request;
- request.method = method;
- request.path = path;
- request.headers = params;
- request.body = data;
+ httplib::Response response;
- httplib::Response response;
+ if (!cli->send(request, response)) {
+ LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
+ return Common::WebResult{Common::WebResult::Code::LibError, "Null response"};
+ }
- if (!cli->send(request, response)) {
- LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
- return Common::WebResult{Common::WebResult::Code::LibError, "Null response"};
- }
+ if (response.status >= 400) {
+ LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
+ response.status);
+ return Common::WebResult{Common::WebResult::Code::HttpError,
+ std::to_string(response.status)};
+ }
- if (response.status >= 400) {
- LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
- response.status);
- return Common::WebResult{Common::WebResult::Code::HttpError,
- std::to_string(response.status)};
- }
+ auto content_type = response.headers.find("content-type");
- auto content_type = response.headers.find("content-type");
+ if (content_type == response.headers.end()) {
+ LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
+ return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
+ }
- if (content_type == response.headers.end()) {
- LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
- return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
+ if (content_type->second.find("application/json") == std::string::npos &&
+ content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
+ LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
+ content_type->second);
+ return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
+ }
+ return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
}
- if (content_type->second.find("application/json") == std::string::npos &&
- content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
- LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
- content_type->second);
- return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
- }
- return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
-}
+ // Retrieve a new JWT from given username and token
+ void UpdateJWT() {
+ if (username.empty() || token.empty()) {
+ return;
+ }
-void Client::UpdateJWT() {
- if (!username.empty() && !token.empty()) {
auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
if (result.result_code != Common::WebResult::Code::Success) {
LOG_ERROR(WebService, "UpdateJWT failed");
@@ -123,27 +158,39 @@ void Client::UpdateJWT() {
jwt_cache.jwt = jwt = result.returned_data;
}
}
-}
-Common::WebResult Client::GenericJson(const std::string& method, const std::string& path,
- const std::string& data, bool allow_anonymous) {
- if (jwt.empty()) {
- UpdateJWT();
- }
+ std::string host;
+ std::string username;
+ std::string token;
+ std::string jwt;
+ std::unique_ptr<httplib::Client> cli;
+
+ struct JWTCache {
+ std::mutex mutex;
+ std::string username;
+ std::string token;
+ std::string jwt;
+ };
+ static inline JWTCache jwt_cache;
+};
- if (jwt.empty() && !allow_anonymous) {
- LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
- return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"};
- }
+Client::Client(std::string host, std::string username, std::string token)
+ : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
- auto result = GenericJson(method, path, data, jwt);
- if (result.result_string == "401") {
- // Try again with new JWT
- UpdateJWT();
- result = GenericJson(method, path, data, jwt);
- }
+Client::~Client() = default;
+
+Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
+ bool allow_anonymous) {
+ return impl->GenericJson("POST", path, data, allow_anonymous);
+}
+
+Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
+ return impl->GenericJson("GET", path, "", allow_anonymous);
+}
- return result;
+Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
+ bool allow_anonymous) {
+ return impl->GenericJson("DELETE", path, data, allow_anonymous);
}
} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index d75fbcc15..c637e09df 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -4,23 +4,19 @@
#pragma once
-#include <functional>
-#include <mutex>
+#include <memory>
#include <string>
-#include <tuple>
-#include <httplib.h>
-#include "common/common_types.h"
-#include "common/web_result.h"
-namespace httplib {
-class Client;
+namespace Common {
+struct WebResult;
}
namespace WebService {
class Client {
public:
- Client(const std::string& host, const std::string& username, const std::string& token);
+ Client(std::string host, std::string username, std::string token);
+ ~Client();
/**
* Posts JSON to the specified path.
@@ -30,9 +26,7 @@ public:
* @return the result of the request.
*/
Common::WebResult PostJson(const std::string& path, const std::string& data,
- bool allow_anonymous) {
- return GenericJson("POST", path, data, allow_anonymous);
- }
+ bool allow_anonymous);
/**
* Gets JSON from the specified path.
@@ -40,9 +34,7 @@ public:
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
* @return the result of the request.
*/
- Common::WebResult GetJson(const std::string& path, bool allow_anonymous) {
- return GenericJson("GET", path, "", allow_anonymous);
- }
+ Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
/**
* Deletes JSON to the specified path.
@@ -52,41 +44,11 @@ public:
* @return the result of the request.
*/
Common::WebResult DeleteJson(const std::string& path, const std::string& data,
- bool allow_anonymous) {
- return GenericJson("DELETE", path, data, allow_anonymous);
- }
+ bool allow_anonymous);
private:
- /// A generic function handles POST, GET and DELETE request together
- Common::WebResult GenericJson(const std::string& method, const std::string& path,
- const std::string& data, bool allow_anonymous);
-
- /**
- * A generic function with explicit authentication method specified
- * JWT is used if the jwt parameter is not empty
- * username + token is used if jwt is empty but username and token are not empty
- * anonymous if all of jwt, username and token are empty
- */
- Common::WebResult GenericJson(const std::string& method, const std::string& path,
- const std::string& data, const std::string& jwt = "",
- const std::string& username = "", const std::string& token = "");
-
- // Retrieve a new JWT from given username and token
- void UpdateJWT();
-
- std::string host;
- std::string username;
- std::string token;
- std::string jwt;
- std::unique_ptr<httplib::Client> cli;
-
- struct JWTCache {
- std::mutex mutex;
- std::string username;
- std::string token;
- std::string jwt;
- };
- static JWTCache jwt_cache;
+ struct Impl;
+ std::unique_ptr<Impl> impl;
};
} // namespace WebService
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index cbcd5dd5f..44d423da2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,8 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
// Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
- auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
- *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height);
+ auto unswizzled_data =
+ Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format),
+ surface_width, surface_height, 1U);
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
surface_width, surface_height);
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 4a09da685..7403e9ccd 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -66,10 +66,11 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
}
};
- add_threads(Core::System::GetInstance().Scheduler(0)->GetThreadList());
- add_threads(Core::System::GetInstance().Scheduler(1)->GetThreadList());
- add_threads(Core::System::GetInstance().Scheduler(2)->GetThreadList());
- add_threads(Core::System::GetInstance().Scheduler(3)->GetThreadList());
+ const auto& system = Core::System::GetInstance();
+ add_threads(system.Scheduler(0).GetThreadList());
+ add_threads(system.Scheduler(1).GetThreadList());
+ add_threads(system.Scheduler(2).GetThreadList());
+ add_threads(system.Scheduler(3).GetThreadList());
return item_list;
}
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 8f99a1c78..3881aba5f 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -96,7 +96,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
FileSys::ContentRecordType::Program);
for (const auto& game : installed_games) {
- const auto& file = cache->GetEntryUnparsed(game);
+ const auto file = cache->GetEntryUnparsed(game);
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
if (!loader)
continue;
@@ -107,7 +107,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
loader->ReadProgramId(program_id);
const FileSys::PatchManager patch{program_id};
- const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
+ const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
if (control != nullptr)
GetMetadataFromControlNCA(patch, *control, icon, name);
@@ -135,9 +135,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {
FileSys::ContentRecordType::Control);
for (const auto& entry : control_data) {
- const auto nca = cache->GetEntry(entry);
- if (nca != nullptr)
- nca_control_map.insert_or_assign(entry.title_id, nca);
+ auto nca = cache->GetEntry(entry);
+ if (nca != nullptr) {
+ nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
+ }
}
}
@@ -153,9 +154,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
QFileInfo file_info(physical_name.c_str());
if (!is_dir && file_info.suffix().toStdString() == "nca") {
auto nca =
- std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
- if (nca->GetType() == FileSys::NCAContentType::Control)
- nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
+ std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
+ if (nca->GetType() == FileSys::NCAContentType::Control) {
+ const u64 title_id = nca->GetTitleId();
+ nca_control_map.insert_or_assign(title_id, std::move(nca));
+ }
}
return true;
};
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 09d20c42f..0e42d0bde 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -63,7 +63,7 @@ private:
void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
std::shared_ptr<FileSys::VfsFilesystem> vfs;
- std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
+ std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;
QStringList watch_list;
QString dir_path;
bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e11833c5a..bef9df00d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -31,6 +31,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QMessageBox>
+#include <QtConcurrent/QtConcurrent>
#include <QtGui>
#include <QtWidgets>
#include <fmt/format.h>
@@ -171,8 +172,11 @@ GMainWindow::GMainWindow()
.arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
show();
+ // Gen keys if necessary
+ OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
+
// Necessary to load titles from nand in gamelist.
- Service::FileSystem::CreateFactories(vfs);
+ Service::FileSystem::CreateFactories(*vfs);
game_list->LoadCompatibilityList();
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
@@ -443,6 +447,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help
+ connect(ui.action_Rederive, &QAction::triggered, this,
+ std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
}
@@ -902,22 +908,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
}
void GMainWindow::OnMenuLoadFile() {
- QString extensions;
- for (const auto& piece : game_list->supported_file_extensions)
- extensions += "*." + piece + " ";
-
- extensions += "main ";
+ const QString extensions =
+ QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
+ const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
+ "%1 is an identifier for the Switch executable file extensions.")
+ .arg(extensions);
+ const QString filename = QFileDialog::getOpenFileName(
+ this, tr("Load File"), UISettings::values.roms_path, file_filter);
- QString file_filter = tr("Switch Executable") + " (" + extensions + ")";
- file_filter += ";;" + tr("All Files (*.*)");
-
- QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
- UISettings::values.roms_path, file_filter);
- if (!filename.isEmpty()) {
- UISettings::values.roms_path = QFileInfo(filename).path();
-
- BootGame(filename);
+ if (filename.isEmpty()) {
+ return;
}
+
+ UISettings::values.roms_path = QFileInfo(filename).path();
+ BootGame(filename);
}
void GMainWindow::OnMenuLoadFolder() {
@@ -1133,7 +1137,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
: FileUtil::UserPath::NANDDir,
dir_path.toStdString());
- Service::FileSystem::CreateFactories(vfs);
+ Service::FileSystem::CreateFactories(*vfs);
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
}
}
@@ -1375,6 +1379,86 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
}
}
+void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
+ if (behavior == ReinitializeKeyBehavior::Warning) {
+ const auto res = QMessageBox::information(
+ this, tr("Confirm Key Rederivation"),
+ tr("You are about to force rederive all of your keys. \nIf you do not know what this "
+ "means or what you are doing, \nthis is a potentially destructive action. \nPlease "
+ "make "
+ "sure this is what you want \nand optionally make backups.\n\nThis will delete your "
+ "autogenerated key files and re-run the key derivation module."),
+ QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
+
+ if (res == QMessageBox::Cancel)
+ return;
+
+ FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
+ "prod.keys_autogenerated");
+ FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
+ "console.keys_autogenerated");
+ FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
+ "title.keys_autogenerated");
+ }
+
+ Core::Crypto::KeyManager keys{};
+ if (keys.BaseDeriveNecessary()) {
+ Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
+ FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
+
+ const auto function = [this, &keys, &pdm] {
+ keys.PopulateFromPartitionData(pdm);
+ Service::FileSystem::CreateFactories(*vfs);
+ keys.DeriveETicket(pdm);
+ };
+
+ QString errors;
+
+ if (!pdm.HasFuses())
+ errors += tr("- Missing fuses - Cannot derive SBK\n");
+ if (!pdm.HasBoot0())
+ errors += tr("- Missing BOOT0 - Cannot derive master keys\n");
+ if (!pdm.HasPackage2())
+ errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n");
+ if (!pdm.HasProdInfo())
+ errors += tr("- Missing PRODINFO - Cannot derive title keys\n");
+
+ if (!errors.isEmpty()) {
+
+ QMessageBox::warning(
+ this, tr("Warning Missing Derivation Components"),
+ tr("The following are missing from your configuration that may hinder key "
+ "derivation. It will be attempted but may not complete.<br><br>") +
+ errors +
+ tr("<br><br>You can get all of these and dump all of your games easily by "
+ "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
+ "quickstart guide</a>. Alternatively, you can use another method of dumping "
+ "to obtain all of your keys."));
+ }
+
+ QProgressDialog prog;
+ prog.setRange(0, 0);
+ prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
+ "system's performance."));
+ prog.setWindowTitle(tr("Deriving Keys"));
+
+ prog.show();
+
+ auto future = QtConcurrent::run(function);
+ while (!future.isFinished()) {
+ QCoreApplication::processEvents();
+ }
+
+ prog.close();
+ }
+
+ Service::FileSystem::CreateFactories(*vfs);
+
+ if (behavior == ReinitializeKeyBehavior::Warning) {
+ game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
+ }
+}
+
bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true;
@@ -1483,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {
emit UpdateThemedIcons();
}
-void GMainWindow::SetDiscordEnabled(bool state) {
+void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#ifdef USE_DISCORD_PRESENCE
if (state) {
discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index fe0e9a50a..3663d6aed 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -41,6 +41,11 @@ enum class EmulatedDirectoryTarget {
SDMC,
};
+enum class ReinitializeKeyBehavior {
+ NoWarning,
+ Warning,
+};
+
namespace DiscordRPC {
class DiscordInterface;
}
@@ -167,6 +172,7 @@ private slots:
void HideFullscreen();
void ToggleWindowMode();
void OnCoreError(Core::System::ResultStatus, std::string);
+ void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
private:
void UpdateStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index cb1664b21..9851f507d 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -103,6 +103,7 @@
</property>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
+ <addaction name="action_Rederive"/>
<addaction name="action_About"/>
</widget>
<addaction name="menu_File"/>
@@ -159,6 +160,11 @@
<string>&amp;Stop</string>
</property>
</action>
+ <action name="action_Rederive">
+ <property name="text">
+ <string>Reinitialize keys...</string>
+ </property>
+ </action>
<action name="action_About">
<property name="text">
<string>About yuzu</string>
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 27aba95f6..c8b93b85b 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -175,7 +175,7 @@ int main(int argc, char** argv) {
Core::System& system{Core::System::GetInstance()};
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
- Service::FileSystem::CreateFactories(system.GetFilesystem());
+ Service::FileSystem::CreateFactories(*system.GetFilesystem());
SCOPE_EXIT({ system.Shutdown(); });