summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt22
-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.cpp46
-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.cpp39
-rw-r--r--src/core/crypto/key_manager.h2
-rw-r--r--src/core/crypto/partition_data_manager.cpp81
-rw-r--r--src/core/crypto/partition_data_manager.h18
-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/card_image.cpp10
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/content_archive.cpp533
-rw-r--r--src/core/file_sys/content_archive.h19
-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.cpp12
-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/savedata_factory.cpp13
-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/gdbstub/gdbstub.cpp6
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp10
-rw-r--r--src/core/hle/kernel/process.h21
-rw-r--r--src/core/hle/kernel/svc.cpp108
-rw-r--r--src/core/hle/kernel/svc.h4
-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/kernel/vm_manager.cpp55
-rw-r--r--src/core/hle/kernel/vm_manager.h15
-rw-r--r--src/core/hle/service/am/am.cpp36
-rw-r--r--src/core/hle/service/am/am.h4
-rw-r--r--src/core/hle/service/am/omm.cpp34
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp33
-rw-r--r--src/core/hle/service/aoc/aoc_u.h2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp37
-rw-r--r--src/core/hle/service/es/es.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/hid/controllers/controller_base.cpp30
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h45
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h56
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h63
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h50
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h50
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp436
-rw-r--r--src/core/hle/service/hid/controllers/npad.h287
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h34
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h63
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp45
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h60
-rw-r--r--src/core/hle/service/hid/hid.cpp517
-rw-r--r--src/core/hle/service/hid/hid.h402
-rw-r--r--src/core/hle/service/mm/mm_u.cpp50
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp1
-rw-r--r--src/core/hle/service/nim/nim.cpp17
-rw-r--r--src/core/hle/service/ns/ns.cpp55
-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/set/set_cal.cpp3
-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/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp23
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/core/loader/xci.cpp3
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp4
84 files changed, 2798 insertions, 1247 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4fddaafd1..4755ec822 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -236,6 +236,24 @@ add_library(core STATIC
hle/service/hid/irs.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
+ hle/service/hid/controllers/controller_base.cpp
+ hle/service/hid/controllers/controller_base.h
+ hle/service/hid/controllers/debug_pad.cpp
+ hle/service/hid/controllers/debug_pad.h
+ hle/service/hid/controllers/gesture.cpp
+ hle/service/hid/controllers/gesture.h
+ hle/service/hid/controllers/keyboard.cpp
+ hle/service/hid/controllers/keyboard.h
+ hle/service/hid/controllers/mouse.cpp
+ hle/service/hid/controllers/mouse.h
+ hle/service/hid/controllers/npad.cpp
+ hle/service/hid/controllers/npad.h
+ hle/service/hid/controllers/stubbed.cpp
+ hle/service/hid/controllers/stubbed.h
+ hle/service/hid/controllers/touchscreen.cpp
+ hle/service/hid/controllers/touchscreen.h
+ hle/service/hid/controllers/xpad.cpp
+ hle/service/hid/controllers/xpad.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/ldn.cpp
@@ -400,8 +418,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..7cb86ed92 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() {
@@ -371,8 +375,7 @@ const Kernel::Process* System::CurrentProcess() const {
}
ARM_Interface& System::ArmInterface(std::size_t core_index) {
- ASSERT(core_index < NUM_CPU_CORES);
- return impl->cpu_cores[core_index]->ArmInterface();
+ return CpuCore(core_index).ArmInterface();
}
Cpu& System::CpuCore(std::size_t core_index) {
@@ -380,6 +383,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 a59a7e1f5..fd0786068 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -98,7 +98,7 @@ std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
return keyblob;
}
-void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) {
+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 =
@@ -147,31 +147,38 @@ boost::optional<Key128> DeriveSDSeed() {
"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;
}
@@ -234,7 +241,9 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
return {};
std::vector<u8> buffer(ticket_save.GetSize());
- ticket_save.ReadBytes(buffer.data(), buffer.size());
+ if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
+ return {};
+ }
std::vector<TicketRaw> out;
u32 magic{};
@@ -261,6 +270,9 @@ static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
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);
@@ -268,7 +280,7 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
size_t i = 0;
while (out.size() < target_size) {
out.resize(out.size() + 0x20);
- seed_exp[in_size + 3] = i;
+ seed_exp[in_size + 3] = static_cast<u8>(i);
mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
++i;
}
@@ -299,10 +311,11 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
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'))
+ 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));
@@ -871,9 +884,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
"/system/save/80000000000000e2",
"rb+");
+ const auto blob2 = GetTicketblob(save2);
auto res = GetTicketblob(save1);
- const auto res2 = GetTicketblob(save2);
- std::copy(res2.begin(), res2.end(), std::back_inserter(res));
+ res.insert(res.end(), blob2.begin(), blob2.end());
for (const auto& raw : res) {
const auto pair = ParseTicket(raw, rsa_key);
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index a41abbdfc..cccb3c0ae 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -175,7 +175,7 @@ private:
void WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key);
- void DeriveGeneralPurposeKeys(u8 crypto_revision);
+ 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);
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index d1c04e98d..ed0775444 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -11,7 +11,6 @@
#include <array>
#include <cctype>
#include <cstring>
-#include <boost/optional/optional.hpp>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/common_funcs.h"
@@ -19,7 +18,7 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/crypto/ctr_encryption_layer.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"
@@ -302,10 +301,10 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
return nullptr;
}
-PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir)
+PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
: boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
- fuses(FindFileInDirWithNames(sysdata_dir, "fuse")),
- kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")),
+ 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"),
@@ -314,13 +313,14 @@ PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir)
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()),
- prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {}
+ : package1_decrypted->ReadAllBytes()) {
+}
PartitionDataManager::~PartitionDataManager() = default;
@@ -332,18 +332,19 @@ FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
return boot0;
}
-std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const {
- if (HasBoot0() && index < 32)
+PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
+ std::size_t index) const {
+ if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
return GetEncryptedKeyblobs()[index];
return {};
}
-std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const {
+PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
if (!HasBoot0())
return {};
- std::array<std::array<u8, 176>, 32> out{};
- for (size_t i = 0; i < 0x20; ++i)
+ EncryptedKeyBlobs out{};
+ for (size_t i = 0; i < out.size(); ++i)
boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
return out;
}
@@ -389,7 +390,7 @@ std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
}
-std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const {
+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...",
@@ -446,7 +447,7 @@ bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
return false;
}
-void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys,
+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)],
@@ -456,43 +457,38 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
if (file->ReadObject(&header) != sizeof(Package2Header))
return;
- u8 revision = 0xFF;
+ std::size_t revision = 0xFF;
if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
- for (size_t i = 0; i < package2_keys.size(); ++i) {
- if (AttemptDecrypt(package2_keys[i], header))
+ 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 std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end());
-
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(s1_iv);
+ cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
- // package2_decrypted[static_cast<size_t>(type)] = s1;
-
INIHeader ini;
std::memcpy(&ini, c.data(), sizeof(INIHeader));
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
return;
- std::map<u64, KIPHeader> kips{};
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;
- kips.emplace(offset, kip);
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
@@ -503,33 +499,30 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
continue;
}
- std::vector<u8> text(kip.sections[0].size_compressed);
- std::vector<u8> rodata(kip.sections[1].size_compressed);
- std::vector<u8> data(kip.sections[2].size_compressed);
+ 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});
- u64 offset_sec = sizeof(KIPHeader) + offset;
- std::memcpy(text.data(), c.data() + offset_sec, text.size());
- offset_sec += text.size();
- std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size());
- offset_sec += rodata.size();
- std::memcpy(data.data(), c.data() + offset_sec, data.size());
+ const auto rodata_end = text_end + kip.sections[1].size_compressed;
+ const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
- offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
- kip.sections[1].size_compressed + kip.sections[2].size_compressed;
+ const auto data_end = rodata_end + kip.sections[2].size_compressed;
+ const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
- text = DecompressBLZ(text);
- rodata = DecompressBLZ(rodata);
- data = DecompressBLZ(data);
+ 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());
- std::vector<u8> out(text.size() + rodata.size() + data.size());
- std::memcpy(out.data(), text.data(), text.size());
- std::memcpy(out.data() + text.size(), rodata.data(), rodata.size());
- std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size());
+ offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
+ kip.sections[1].size_compressed + kip.sections[2].size_compressed;
if (name == "FS")
- package2_fs[static_cast<size_t>(type)] = out;
+ package2_fs[static_cast<size_t>(type)] = std::move(out);
else if (name == "spl")
- package2_spl[static_cast<size_t>(type)] = out;
+ package2_spl[static_cast<size_t>(type)] = std::move(out);
}
}
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 45c7fecfa..0ad007c72 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -5,9 +5,7 @@
#pragma once
#include <vector>
-#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/file_sys/vfs_types.h"
namespace Core::Crypto {
@@ -24,15 +22,20 @@ enum class Package2Type {
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;
- explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir);
+ 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;
- std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const;
- std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() 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;
@@ -43,7 +46,7 @@ public:
std::vector<u8> GetPackage1Decrypted() const;
std::array<u8, 0x10> GetMasterKeySource() const;
std::array<u8, 0x10> GetKeyblobMACKeySource() const;
- std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const;
+ std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
// Fuses
bool HasFuses() const;
@@ -57,7 +60,8 @@ public:
// Package2
bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
- void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type);
+ 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(
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/card_image.cpp b/src/core/file_sys/card_image.cpp
index 8f5142a07..ecdd7505b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -122,14 +122,16 @@ u64 XCI::GetProgramTitleID() const {
return secure_partition->GetProgramTitleID();
}
-std::shared_ptr<NCA> XCI::GetProgramNCA() const {
- return program;
+bool XCI::HasProgramNCA() const {
+ return program != nullptr;
}
VirtualFile XCI::GetProgramNCAFile() const {
- if (GetProgramNCA() == nullptr)
+ if (!HasProgramNCA()) {
return nullptr;
- return GetProgramNCA()->GetBaseFile();
+ }
+
+ return program->GetBaseFile();
}
const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index ce514dfa0..48cbef666 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -80,7 +80,7 @@ public:
u64 GetProgramTitleID() const;
- std::shared_ptr<NCA> GetProgramNCA() const;
+ bool HasProgramNCA() const;
VirtualFile GetProgramNCAFile() const;
const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index aa1b3c17d..6c356d85d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -97,11 +97,288 @@ union NCASectionHeader {
};
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
-bool IsValidNCA(const NCAHeader& header) {
+static bool IsValidNCA(const NCAHeader& header) {
// TODO(DarkLordZach): Add NCA2/NCA0 support.
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
+NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
+ : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
+ if (file == nullptr) {
+ status = Loader::ResultStatus::ErrorNullFile;
+ return;
+ }
+
+ if (sizeof(NCAHeader) != file->ReadObject(&header)) {
+ LOG_ERROR(Loader, "File reader errored out during header read.");
+ status = Loader::ResultStatus::ErrorBadNCAHeader;
+ return;
+ }
+
+ if (!HandlePotentialHeaderDecryption()) {
+ return;
+ }
+
+ has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
+ [](char c) { return c != '\0'; });
+
+ const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
+ is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
+ return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
+ });
+
+ if (!ReadSections(sections, bktr_base_ivfc_offset)) {
+ return;
+ }
+
+ status = Loader::ResultStatus::Success;
+}
+
+NCA::~NCA() = default;
+
+bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
+ if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
+ status = Loader::ResultStatus::ErrorNCA2;
+ return false;
+ }
+
+ if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
+ status = Loader::ResultStatus::ErrorNCA0;
+ return false;
+ }
+
+ return true;
+}
+
+bool NCA::HandlePotentialHeaderDecryption() {
+ if (IsValidNCA(header)) {
+ return true;
+ }
+
+ if (!CheckSupportedNCA(header)) {
+ return false;
+ }
+
+ NCAHeader dec_header{};
+ Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
+ keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
+ cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
+ Core::Crypto::Op::Decrypt);
+ if (IsValidNCA(dec_header)) {
+ header = dec_header;
+ encrypted = true;
+ } else {
+ if (!CheckSupportedNCA(dec_header)) {
+ return false;
+ }
+
+ if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
+ status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
+ } else {
+ status = Loader::ResultStatus::ErrorMissingHeaderKey;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
+ const std::ptrdiff_t number_sections =
+ std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
+ [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
+
+ std::vector<NCASectionHeader> sections(number_sections);
+ const auto length_sections = SECTION_HEADER_SIZE * number_sections;
+
+ if (encrypted) {
+ auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
+ Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
+ keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
+ cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
+ Core::Crypto::Op::Decrypt);
+ } else {
+ file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
+ }
+
+ return sections;
+}
+
+bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
+ for (std::size_t i = 0; i < sections.size(); ++i) {
+ const auto& section = sections[i];
+
+ if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
+ if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
+ return false;
+ }
+ } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
+ if (!ReadPFS0Section(section, header.section_tables[i])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
+ u64 bktr_base_ivfc_offset) {
+ const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
+ ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
+ const std::size_t romfs_offset = base_offset + ivfc_offset;
+ const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
+ auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
+ auto dec = Decrypt(section, raw, romfs_offset);
+
+ if (dec == nullptr) {
+ if (status != Loader::ResultStatus::Success)
+ return false;
+ if (has_rights_id)
+ status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
+ else
+ status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
+ return false;
+ }
+
+ if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
+ if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
+ section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
+ status = Loader::ResultStatus::ErrorBadBKTRHeader;
+ return false;
+ }
+
+ if (section.bktr.relocation.offset + section.bktr.relocation.size !=
+ section.bktr.subsection.offset) {
+ status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
+ return false;
+ }
+
+ const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
+ if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
+ status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
+ return false;
+ }
+
+ const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
+ RelocationBlock relocation_block{};
+ if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
+ sizeof(RelocationBlock)) {
+ status = Loader::ResultStatus::ErrorBadRelocationBlock;
+ return false;
+ }
+ SubsectionBlock subsection_block{};
+ if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
+ sizeof(RelocationBlock)) {
+ status = Loader::ResultStatus::ErrorBadSubsectionBlock;
+ return false;
+ }
+
+ std::vector<RelocationBucketRaw> relocation_buckets_raw(
+ (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
+ if (dec->ReadBytes(relocation_buckets_raw.data(),
+ section.bktr.relocation.size - sizeof(RelocationBlock),
+ section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
+ section.bktr.relocation.size - sizeof(RelocationBlock)) {
+ status = Loader::ResultStatus::ErrorBadRelocationBuckets;
+ return false;
+ }
+
+ std::vector<SubsectionBucketRaw> subsection_buckets_raw(
+ (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
+ if (dec->ReadBytes(subsection_buckets_raw.data(),
+ section.bktr.subsection.size - sizeof(SubsectionBlock),
+ section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
+ section.bktr.subsection.size - sizeof(SubsectionBlock)) {
+ status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
+ return false;
+ }
+
+ std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
+ std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
+ relocation_buckets.begin(), &ConvertRelocationBucketRaw);
+ std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
+ std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
+ subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
+
+ u32 ctr_low;
+ std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
+ subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
+ subsection_buckets.back().entries.push_back({size, {0}, 0});
+
+ boost::optional<Core::Crypto::Key128> key = boost::none;
+ if (encrypted) {
+ if (has_rights_id) {
+ status = Loader::ResultStatus::Success;
+ key = GetTitlekey();
+ if (key == boost::none) {
+ status = Loader::ResultStatus::ErrorMissingTitlekey;
+ return false;
+ }
+ } else {
+ key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
+ if (key == boost::none) {
+ status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
+ return false;
+ }
+ }
+ }
+
+ if (bktr_base_romfs == nullptr) {
+ status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
+ return false;
+ }
+
+ auto bktr = std::make_shared<BKTR>(
+ bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
+ relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
+ encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
+ section.raw.section_ctr);
+
+ // BKTR applies to entire IVFC, so make an offset version to level 6
+ files.push_back(std::make_shared<OffsetVfsFile>(
+ bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
+ } else {
+ files.push_back(std::move(dec));
+ }
+
+ romfs = files.back();
+ return true;
+}
+
+bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
+ const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
+ section.pfs0.pfs0_header_offset;
+ const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
+
+ auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
+ if (dec != nullptr) {
+ auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
+
+ if (npfs->GetStatus() == Loader::ResultStatus::Success) {
+ dirs.push_back(std::move(npfs));
+ if (IsDirectoryExeFS(dirs.back()))
+ exefs = dirs.back();
+ } else {
+ if (has_rights_id)
+ status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
+ else
+ status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
+ return false;
+ }
+ } else {
+ if (status != Loader::ResultStatus::Success)
+ return false;
+ if (has_rights_id)
+ status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
+ else
+ status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
+ return false;
+ }
+
+ return true;
+}
+
u8 NCA::GetCryptoRevision() const {
u8 master_key_id = header.crypto_type;
if (header.crypto_type_2 > master_key_id)
@@ -133,7 +410,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
static_cast<u8>(type));
u128 out_128{};
memcpy(out_128.data(), out.data(), 16);
- LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
+ LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
master_key_id, header.key_index, out_128[1], out_128[0]);
return out;
@@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
return titlekey;
}
-VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) {
+VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
if (!encrypted)
return in;
@@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
}
}
-NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
- : file(std::move(file_)),
- bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
- status = Loader::ResultStatus::Success;
-
- if (file == nullptr) {
- status = Loader::ResultStatus::ErrorNullFile;
- return;
- }
-
- if (sizeof(NCAHeader) != file->ReadObject(&header)) {
- LOG_ERROR(Loader, "File reader errored out during header read.");
- status = Loader::ResultStatus::ErrorBadNCAHeader;
- return;
- }
-
- encrypted = false;
-
- if (!IsValidNCA(header)) {
- if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
- status = Loader::ResultStatus::ErrorNCA2;
- return;
- }
- if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
- status = Loader::ResultStatus::ErrorNCA0;
- return;
- }
-
- NCAHeader dec_header{};
- Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
- keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
- cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
- Core::Crypto::Op::Decrypt);
- if (IsValidNCA(dec_header)) {
- header = dec_header;
- encrypted = true;
- } else {
- if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
- status = Loader::ResultStatus::ErrorNCA2;
- return;
- }
- if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
- status = Loader::ResultStatus::ErrorNCA0;
- return;
- }
-
- if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
- status = Loader::ResultStatus::ErrorMissingHeaderKey;
- else
- status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
- return;
- }
- }
-
- has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
- [](char c) { return c == '\0'; }) != header.rights_id.end();
-
- const std::ptrdiff_t number_sections =
- std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
- [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
-
- std::vector<NCASectionHeader> sections(number_sections);
- const auto length_sections = SECTION_HEADER_SIZE * number_sections;
-
- if (encrypted) {
- auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
- Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
- keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
- cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
- Core::Crypto::Op::Decrypt);
- } else {
- file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
- }
-
- is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
- return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
- }) != sections.end();
- ivfc_offset = 0;
-
- for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
- auto section = sections[i];
-
- if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
- const std::size_t base_offset =
- header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
- ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
- const std::size_t romfs_offset = base_offset + ivfc_offset;
- const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
- auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
- auto dec = Decrypt(section, raw, romfs_offset);
-
- if (dec == nullptr) {
- if (status != Loader::ResultStatus::Success)
- return;
- if (has_rights_id)
- status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
- else
- status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
- return;
- }
-
- if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
- if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
- section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
- status = Loader::ResultStatus::ErrorBadBKTRHeader;
- return;
- }
-
- if (section.bktr.relocation.offset + section.bktr.relocation.size !=
- section.bktr.subsection.offset) {
- status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
- return;
- }
-
- const u64 size =
- MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
- header.section_tables[i].media_offset);
- if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
- status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
- return;
- }
-
- const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
- RelocationBlock relocation_block{};
- if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
- sizeof(RelocationBlock)) {
- status = Loader::ResultStatus::ErrorBadRelocationBlock;
- return;
- }
- SubsectionBlock subsection_block{};
- if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
- sizeof(RelocationBlock)) {
- status = Loader::ResultStatus::ErrorBadSubsectionBlock;
- return;
- }
-
- std::vector<RelocationBucketRaw> relocation_buckets_raw(
- (section.bktr.relocation.size - sizeof(RelocationBlock)) /
- sizeof(RelocationBucketRaw));
- if (dec->ReadBytes(relocation_buckets_raw.data(),
- section.bktr.relocation.size - sizeof(RelocationBlock),
- section.bktr.relocation.offset + sizeof(RelocationBlock) -
- offset) !=
- section.bktr.relocation.size - sizeof(RelocationBlock)) {
- status = Loader::ResultStatus::ErrorBadRelocationBuckets;
- return;
- }
-
- std::vector<SubsectionBucketRaw> subsection_buckets_raw(
- (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
- sizeof(SubsectionBucketRaw));
- if (dec->ReadBytes(subsection_buckets_raw.data(),
- section.bktr.subsection.size - sizeof(SubsectionBlock),
- section.bktr.subsection.offset + sizeof(SubsectionBlock) -
- offset) !=
- section.bktr.subsection.size - sizeof(SubsectionBlock)) {
- status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
- return;
- }
-
- std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
- std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
- relocation_buckets.begin(), &ConvertRelocationBucketRaw);
- std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
- std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
- subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
-
- u32 ctr_low;
- std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
- subsection_buckets.back().entries.push_back(
- {section.bktr.relocation.offset, {0}, ctr_low});
- subsection_buckets.back().entries.push_back({size, {0}, 0});
-
- boost::optional<Core::Crypto::Key128> key = boost::none;
- if (encrypted) {
- if (has_rights_id) {
- status = Loader::ResultStatus::Success;
- key = GetTitlekey();
- if (key == boost::none) {
- status = Loader::ResultStatus::ErrorMissingTitlekey;
- return;
- }
- } else {
- key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
- if (key == boost::none) {
- status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
- return;
- }
- }
- }
-
- if (bktr_base_romfs == nullptr) {
- status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
- return;
- }
-
- auto bktr = std::make_shared<BKTR>(
- bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
- relocation_block, relocation_buckets, subsection_block, subsection_buckets,
- encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
- bktr_base_ivfc_offset, section.raw.section_ctr);
-
- // BKTR applies to entire IVFC, so make an offset version to level 6
-
- files.push_back(std::make_shared<OffsetVfsFile>(
- bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
- romfs = files.back();
- } else {
- files.push_back(std::move(dec));
- romfs = files.back();
- }
- } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
- u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
- MEDIA_OFFSET_MULTIPLIER) +
- section.pfs0.pfs0_header_offset;
- u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
- header.section_tables[i].media_offset);
- auto dec =
- Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
- if (dec != nullptr) {
- auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
-
- if (npfs->GetStatus() == Loader::ResultStatus::Success) {
- dirs.push_back(std::move(npfs));
- if (IsDirectoryExeFS(dirs.back()))
- exefs = dirs.back();
- } else {
- if (has_rights_id)
- status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
- else
- status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
- return;
- }
- } else {
- if (status != Loader::ResultStatus::Success)
- return;
- if (has_rights_id)
- status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
- else
- status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
- return;
- }
- }
- }
-
- status = Loader::ResultStatus::Success;
-}
-
-NCA::~NCA() = default;
-
Loader::ResultStatus NCA::GetStatus() const {
return status;
}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f9f66cae9..1c903cd3f 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
-bool IsValidNCA(const NCAHeader& header);
-
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
@@ -106,10 +104,19 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
+ bool CheckSupportedNCA(const NCAHeader& header);
+ bool HandlePotentialHeaderDecryption();
+
+ std::vector<NCASectionHeader> ReadSectionHeaders() const;
+ bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
+ bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
+ u64 bktr_base_ivfc_offset);
+ bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
+
u8 GetCryptoRevision() const;
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
boost::optional<Core::Crypto::Key128> GetTitlekey();
- VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset);
+ VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
std::vector<VirtualDir> dirs;
std::vector<VirtualFile> files;
@@ -118,15 +125,15 @@ private:
VirtualDir exefs = nullptr;
VirtualFile file;
VirtualFile bktr_base_romfs;
- u64 ivfc_offset;
+ u64 ivfc_offset = 0;
NCAHeader header{};
bool has_rights_id{};
Loader::ResultStatus status{};
- bool encrypted;
- bool is_update;
+ bool encrypted = false;
+ bool is_update = false;
Core::Crypto::KeyManager keys;
};
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 019caebe9..0117cb0bf 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -214,8 +214,14 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
VirtualFile update_raw) const {
- LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
- static_cast<u8>(type));
+ const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
+ title_id, static_cast<u8>(type))
+ .c_str();
+
+ if (type == ContentRecordType::Program)
+ LOG_INFO(Loader, log_string);
+ else
+ LOG_DEBUG(Loader, log_string);
if (romfs == nullptr)
return romfs;
@@ -346,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/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 47f2ab9e0..ef1aaebbb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
meta.title_id);
}
+ if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
+ LOG_WARNING(Service_FS,
+ "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
+ "non-zero ({:016X}{:016X})",
+ meta.user_id[1], meta.user_id[0]);
+ }
+
std::string save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
case SaveDataSpaceId::NandUser:
out = "/user/";
break;
+ case SaveDataSpaceId::TemporaryStorage:
+ out = "/temp/";
+ break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
}
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
case SaveDataType::SystemSaveData:
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
+ case SaveDataType::DeviceSaveData:
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
case SaveDataType::TemporaryStorage:
- return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
+ return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
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/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/process.cpp b/src/core/hle/kernel/process.cpp
index c80b2c507..073dd5a7d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -153,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());
}
/**
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 73ec01e11..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,9 +58,23 @@ 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 {
struct Segment {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e406df829..3b8a2e230 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -8,6 +8,7 @@
#include <mutex>
#include <vector>
+#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -36,9 +37,6 @@
namespace Kernel {
namespace {
-constexpr bool Is4KBAligned(VAddr address) {
- return (address & 0xFFF) == 0;
-}
// Checks if address + size is greater than the given address
// This can return false if the size causes an overflow of a 64-bit type
@@ -69,11 +67,11 @@ bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
- if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
+ if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Is4KBAligned(size)) {
+ if (size == 0 || !Common::Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
@@ -352,6 +350,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
return ERR_INVALID_ADDRESS_STATE;
}
+ if (!Common::IsWordAligned(mutex_addr)) {
+ return ERR_INVALID_ADDRESS;
+ }
+
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
@@ -365,6 +367,10 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return ERR_INVALID_ADDRESS_STATE;
}
+ if (!Common::IsWordAligned(mutex_addr)) {
+ return ERR_INVALID_ADDRESS;
+ }
+
return Mutex::Release(mutex_addr);
}
@@ -389,6 +395,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();
}
}
@@ -442,25 +454,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
case GetInfoType::RandomEntropy:
*result = 0;
break;
- case GetInfoType::AddressSpaceBaseAddr:
- *result = vm_manager.GetCodeRegionBaseAddress();
+ case GetInfoType::ASLRRegionBaseAddr:
+ *result = vm_manager.GetASLRRegionBaseAddress();
break;
- case GetInfoType::AddressSpaceSize: {
- const u64 width = vm_manager.GetAddressSpaceWidth();
-
- switch (width) {
- case 32:
- *result = 0xFFE00000;
- break;
- case 36:
- *result = 0xFF8000000;
- break;
- case 39:
- *result = 0x7FF8000000;
- break;
- }
+ case GetInfoType::ASLRRegionSize:
+ *result = vm_manager.GetASLRRegionSize();
break;
- }
case GetInfoType::NewMapRegionBaseAddr:
*result = vm_manager.GetNewMapRegionBaseAddress();
break;
@@ -577,14 +576,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shared_memory_handle, addr, size, permissions);
- if (!Is4KBAligned(addr)) {
+ if (!Common::Is4KBAligned(addr)) {
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Is4KBAligned(size)) {
+ if (size == 0 || !Common::Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
+ if (!IsValidAddressRange(addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
const auto permissions_type = static_cast<MemoryPermission>(permissions);
if (permissions_type != MemoryPermission::Read &&
permissions_type != MemoryPermission::ReadWrite) {
@@ -598,26 +601,46 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return ERR_INVALID_HANDLE;
}
- return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type,
- MemoryPermission::DontCare);
+ auto* const current_process = Core::CurrentProcess();
+ const auto& vm_manager = current_process->VMManager();
+
+ if (!vm_manager.IsWithinASLRRegion(addr, size)) {
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
}
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
shared_memory_handle, addr, size);
- if (!Is4KBAligned(addr)) {
+ if (!Common::Is4KBAligned(addr)) {
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Is4KBAligned(size)) {
+ if (size == 0 || !Common::Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
+ if (!IsValidAddressRange(addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
+ if (!shared_memory) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ auto* const current_process = Core::CurrentProcess();
+ const auto& vm_manager = current_process->VMManager();
- return shared_memory->Unmap(Core::CurrentProcess(), addr);
+ if (!vm_manager.IsWithinASLRRegion(addr, size)) {
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return shared_memory->Unmap(current_process, addr);
}
/// Query process memory
@@ -803,7 +826,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 +1115,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 +1273,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.h b/src/core/hle/kernel/svc.h
index 70148c4fe..554a5e328 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -41,8 +41,8 @@ enum class GetInfoType : u64 {
RandomEntropy = 11,
PerformanceCounter = 0xF0000002,
// 2.0.0+
- AddressSpaceBaseAddr = 12,
- AddressSpaceSize = 13,
+ ASLRRegionBaseAddr = 12,
+ ASLRRegionSize = 13,
NewMapRegionBaseAddr = 14,
NewMapRegionSize = 15,
// 3.0.0+
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/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..e1a34eef1 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -393,30 +393,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
switch (type) {
case FileSys::ProgramAddressSpaceType::Is32Bit:
+ case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
address_space_width = 32;
code_region_base = 0x200000;
code_region_end = code_region_base + 0x3FE00000;
- map_region_size = 0x40000000;
- heap_region_size = 0x40000000;
+ aslr_region_base = 0x200000;
+ aslr_region_end = aslr_region_base + 0xFFE00000;
+ if (type == FileSys::ProgramAddressSpaceType::Is32Bit) {
+ map_region_size = 0x40000000;
+ heap_region_size = 0x40000000;
+ } else {
+ map_region_size = 0;
+ heap_region_size = 0x80000000;
+ }
break;
case FileSys::ProgramAddressSpaceType::Is36Bit:
address_space_width = 36;
code_region_base = 0x8000000;
code_region_end = code_region_base + 0x78000000;
+ aslr_region_base = 0x8000000;
+ aslr_region_end = aslr_region_base + 0xFF8000000;
map_region_size = 0x180000000;
heap_region_size = 0x180000000;
break;
- case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
- address_space_width = 32;
- code_region_base = 0x200000;
- code_region_end = code_region_base + 0x3FE00000;
- map_region_size = 0;
- heap_region_size = 0x80000000;
- break;
case FileSys::ProgramAddressSpaceType::Is39Bit:
address_space_width = 39;
code_region_base = 0x8000000;
code_region_end = code_region_base + 0x80000000;
+ aslr_region_base = 0x8000000;
+ aslr_region_end = aslr_region_base + 0x7FF8000000;
map_region_size = 0x1000000000;
heap_region_size = 0x180000000;
new_map_region_size = 0x80000000;
@@ -490,6 +495,38 @@ u64 VMManager::GetAddressSpaceWidth() const {
return address_space_width;
}
+VAddr VMManager::GetASLRRegionBaseAddress() const {
+ return aslr_region_base;
+}
+
+VAddr VMManager::GetASLRRegionEndAddress() const {
+ return aslr_region_end;
+}
+
+u64 VMManager::GetASLRRegionSize() const {
+ return aslr_region_end - aslr_region_base;
+}
+
+bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
+ const VAddr range_end = begin + size;
+ const VAddr aslr_start = GetASLRRegionBaseAddress();
+ const VAddr aslr_end = GetASLRRegionEndAddress();
+
+ if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
+ return false;
+ }
+
+ if (range_end > heap_region_base && heap_region_end > begin) {
+ return false;
+ }
+
+ if (range_end > map_region_base && map_region_end > begin) {
+ return false;
+ }
+
+ return true;
+}
+
VAddr VMManager::GetCodeRegionBaseAddress() const {
return code_region_base;
}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 015559a64..84c890224 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -205,6 +205,18 @@ public:
/// Gets the address space width in bits.
u64 GetAddressSpaceWidth() const;
+ /// Gets the base address of the ASLR region.
+ VAddr GetASLRRegionBaseAddress() const;
+
+ /// Gets the end address of the ASLR region.
+ VAddr GetASLRRegionEndAddress() const;
+
+ /// Determines whether or not the specified address range is within the ASLR region.
+ bool IsWithinASLRRegion(VAddr address, u64 size) const;
+
+ /// Gets the size of the ASLR region
+ u64 GetASLRRegionSize() const;
+
/// Gets the base address of the code region.
VAddr GetCodeRegionBaseAddress() const;
@@ -306,6 +318,9 @@ private:
VAddr address_space_base = 0;
VAddr address_space_end = 0;
+ VAddr aslr_region_base = 0;
+ VAddr aslr_region_end = 0;
+
VAddr code_region_base = 0;
VAddr code_region_end = 0;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 69bfce1c1..4d1f83170 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -638,10 +638,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{25, nullptr, "ExtendSaveData"},
{26, nullptr, "GetSaveDataSize"},
- {30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"},
- {31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"},
- {32, nullptr, "BeginBlockingHomeButton"},
- {33, nullptr, "EndBlockingHomeButton"},
+ {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed,
+ "BeginBlockingHomeButtonShortAndLongPressed"},
+ {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed,
+ "EndBlockingHomeButtonShortAndLongPressed"},
+ {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
+ {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -669,6 +671,32 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
IApplicationFunctions::~IApplicationFunctions() = default;
+void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
+ Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
+void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
+ Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
+void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
+void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
constexpr std::array<u8, 0x88> data{{
0xca, 0x97, 0x94, 0xc7, // Magic
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b39b0d838..095f94851 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -154,6 +154,10 @@ private:
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
+ void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
+ void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
+ void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
+ void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
index 1c37f849f..6ab3fb906 100644
--- a/src/core/hle/service/am/omm.cpp
+++ b/src/core/hle/service/am/omm.cpp
@@ -17,22 +17,24 @@ OMM::OMM() : ServiceFramework{"omm"} {
{5, nullptr, "GetCradleStatus"},
{6, nullptr, "FadeInDisplay"},
{7, nullptr, "FadeOutDisplay"},
- {8, nullptr, "Unknown1"},
- {9, nullptr, "Unknown2"},
- {10, nullptr, "Unknown3"},
- {11, nullptr, "Unknown4"},
- {12, nullptr, "Unknown5"},
- {13, nullptr, "Unknown6"},
- {14, nullptr, "Unknown7"},
- {15, nullptr, "Unknown8"},
- {16, nullptr, "Unknown9"},
- {17, nullptr, "Unknown10"},
- {18, nullptr, "Unknown11"},
- {19, nullptr, "Unknown12"},
- {20, nullptr, "Unknown13"},
- {21, nullptr, "Unknown14"},
- {22, nullptr, "Unknown15"},
- {23, nullptr, "Unknown16"},
+ {8, nullptr, "GetCradleFwVersion"},
+ {9, nullptr, "NotifyCecSettingsChanged"},
+ {10, nullptr, "SetOperationModePolicy"},
+ {11, nullptr, "GetDefaultDisplayResolution"},
+ {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"},
+ {13, nullptr, "UpdateDefaultDisplayResolution"},
+ {14, nullptr, "ShouldSleepOnBoot"},
+ {15, nullptr, "NotifyHdcpApplicationExecutionStarted"},
+ {16, nullptr, "NotifyHdcpApplicationExecutionFinished"},
+ {17, nullptr, "NotifyHdcpApplicationDrawingStarted"},
+ {18, nullptr, "NotifyHdcpApplicationDrawingFinished"},
+ {19, nullptr, "GetHdcpAuthenticationFailedEvent"},
+ {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"},
+ {21, nullptr, "SetHdcpAuthenticationFailedEmulation"},
+ {22, nullptr, "GetHdcpStateChangeEvent"},
+ {23, nullptr, "GetHdcpState"},
+ {24, nullptr, "ShowCardUpdateProcessing"},
+ {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
};
// clang-format on
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0ecfb5af1..428069df2 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,10 +7,13 @@
#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/event.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -19,7 +22,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;
@@ -53,9 +56,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
{5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},
{6, nullptr, "PrepareAddOnContentByApplicationId"},
{7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
- {8, nullptr, "GetAddOnContentListChangedEvent"},
+ {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
};
RegisterHandlers(functions);
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
+ "GetAddOnContentListChanged:Event");
}
AOC_U::~AOC_U() = default;
@@ -97,14 +104,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) {
@@ -118,6 +135,14 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
+void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AOC, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(aoc_change_event);
+}
+
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AOC_U>()->InstallAsService(service_manager);
}
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index b3c7cab7a..68d94fdaa 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -18,8 +18,10 @@ private:
void ListAddOnContent(Kernel::HLERequestContext& ctx);
void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx);
void PrepareAddOnContent(Kernel::HLERequestContext& ctx);
+ void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
std::vector<u64> add_on_content;
+ Kernel::SharedPtr<Kernel::Event> aoc_change_event;
};
/// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6073f4ecd..fac6785a5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -22,20 +22,22 @@ class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
: ServiceFramework("IAudioRenderer") {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"},
- {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"},
- {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"},
- {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"},
- {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
- {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
- {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
+ {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
+ {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
+ {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
+ {3, &IAudioRenderer::GetState, "GetState"},
+ {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
+ {5, &IAudioRenderer::Start, "Start"},
+ {6, &IAudioRenderer::Stop, "Stop"},
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
- {8, nullptr, "SetAudioRendererRenderingTimeLimit"},
- {9, nullptr, "GetAudioRendererRenderingTimeLimit"},
- {10, nullptr, "RequestUpdateAudioRendererAuto"},
+ {8, nullptr, "SetRenderingTimeLimit"},
+ {9, nullptr, "GetRenderingTimeLimit"},
+ {10, nullptr, "RequestUpdateAuto"},
{11, nullptr, "ExecuteAudioRendererRendering"},
};
+ // clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
@@ -49,42 +51,42 @@ private:
system_event->Signal();
}
- void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) {
+ void GetSampleRate(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(renderer->GetSampleRate());
LOG_DEBUG(Service_Audio, "called");
}
- void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) {
+ void GetSampleCount(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(renderer->GetSampleCount());
LOG_DEBUG(Service_Audio, "called");
}
- void GetAudioRendererState(Kernel::HLERequestContext& ctx) {
+ void GetState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
LOG_DEBUG(Service_Audio, "called");
}
- void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) {
+ void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(renderer->GetMixBufferCount());
LOG_DEBUG(Service_Audio, "called");
}
- void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
+ void RequestUpdate(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
- void StartAudioRenderer(Kernel::HLERequestContext& ctx) {
+ void Start(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -92,7 +94,7 @@ private:
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
- void StopAudioRenderer(Kernel::HLERequestContext& ctx) {
+ void Stop(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -129,6 +131,7 @@ public:
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
{11, nullptr, "QueryAudioDeviceInputEvent"},
{12, nullptr, "QueryAudioDeviceOutputEvent"},
+ {13, nullptr, "GetAudioSystemMasterVolumeSetting"},
};
RegisterHandlers(functions);
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index d40f18565..6701cb913 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -9,6 +9,7 @@ namespace Service::ES {
class ETicket final : public ServiceFramework<ETicket> {
public:
explicit ETicket() : ServiceFramework{"es"} {
+ // clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "ImportTicket"},
{2, nullptr, "ImportTicketCertificateSet"},
@@ -37,15 +38,18 @@ public:
{25, nullptr, "DeletePrepurchaseRecord"},
{26, nullptr, "DeleteAllPrepurchaseRecord"},
{27, nullptr, "CountPrepurchaseRecord"},
- {28, nullptr, "ListPrepurchaseRecord"},
+ {28, nullptr, "ListPrepurchaseRecordRightsIds"},
{29, nullptr, "ListPrepurchaseRecordInfo"},
- {30, nullptr, "Unknown1"},
- {31, nullptr, "Unknown2"},
- {32, nullptr, "Unknown3"},
- {33, nullptr, "Unknown4"},
- {34, nullptr, "Unknown5"},
- {35, nullptr, "Unknown6"},
+ {30, nullptr, "CountTicket"},
+ {31, nullptr, "ListTicketRightsIds"},
+ {32, nullptr, "CountPrepurchaseRecordEx"},
+ {33, nullptr, "ListPrepurchaseRecordRightsIdsEx"},
+ {34, nullptr, "GetEncryptedTicketSize"},
+ {35, nullptr, "GetEncryptedTicketData"},
+ {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
+ {503, nullptr, "GetTitleKey"},
};
+ // clang-format on
RegisterHandlers(functions);
}
};
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/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
new file mode 100644
index 000000000..0993a7815
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -0,0 +1,30 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+
+ControllerBase::ControllerBase() = default;
+ControllerBase::~ControllerBase() = default;
+
+void ControllerBase::ActivateController() {
+ if (is_activated) {
+ OnRelease();
+ }
+ is_activated = true;
+ OnInit();
+}
+
+void ControllerBase::DeactivateController() {
+ if (is_activated) {
+ OnRelease();
+ }
+ is_activated = false;
+}
+
+bool ControllerBase::IsControllerActivated() const {
+ return is_activated;
+}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
new file mode 100644
index 000000000..f0e092b1b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -0,0 +1,45 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace Service::HID {
+class ControllerBase {
+public:
+ ControllerBase();
+ virtual ~ControllerBase();
+
+ // Called when the controller is initialized
+ virtual void OnInit() = 0;
+
+ // When the controller is released
+ virtual void OnRelease() = 0;
+
+ // When the controller is requesting an update for the shared memory
+ virtual void OnUpdate(u8* data, std::size_t size) = 0;
+
+ // Called when input devices should be loaded
+ virtual void OnLoadInputDevices() = 0;
+
+ void ActivateController();
+
+ void DeactivateController();
+
+ bool IsControllerActivated() const;
+
+protected:
+ bool is_activated{false};
+
+ struct CommonHeader {
+ s64_le timestamp;
+ s64_le total_entry_count;
+ s64_le last_entry_index;
+ s64_le entry_count;
+ };
+ static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
new file mode 100644
index 000000000..3d100763f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -0,0 +1,42 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/common_types.h"
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/debug_pad.h"
+
+namespace Service::HID {
+
+Controller_DebugPad::Controller_DebugPad() = default;
+Controller_DebugPad::~Controller_DebugPad() = default;
+
+void Controller_DebugPad::OnInit() {}
+
+void Controller_DebugPad::OnRelease() {}
+
+void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
+ shared_memory.header.timestamp = CoreTiming::GetTicks();
+ shared_memory.header.total_entry_count = 17;
+
+ if (!IsControllerActivated()) {
+ shared_memory.header.entry_count = 0;
+ shared_memory.header.last_entry_index = 0;
+ return;
+ }
+ shared_memory.header.entry_count = 16;
+
+ const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
+ shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+ auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
+
+ cur_entry.sampling_number = last_entry.sampling_number + 1;
+ cur_entry.sampling_number2 = cur_entry.sampling_number;
+ // TODO(ogniK): Update debug pad states
+
+ std::memcpy(data, &shared_memory, sizeof(SharedMemory));
+}
+
+void Controller_DebugPad::OnLoadInputDevices() {}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
new file mode 100644
index 000000000..62b4f2682
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -0,0 +1,56 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+class Controller_DebugPad final : public ControllerBase {
+public:
+ Controller_DebugPad();
+ ~Controller_DebugPad() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, std::size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+private:
+ struct AnalogStick {
+ s32_le x;
+ s32_le y;
+ };
+ static_assert(sizeof(AnalogStick) == 0x8);
+
+ struct PadStates {
+ s64_le sampling_number;
+ s64_le sampling_number2;
+ u32_le attribute;
+ u32_le button_state;
+ AnalogStick r_stick;
+ AnalogStick l_stick;
+ };
+ static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
+
+ struct SharedMemory {
+ CommonHeader header;
+ std::array<PadStates, 17> pad_states;
+ INSERT_PADDING_BYTES(0x138);
+ };
+ static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
+ SharedMemory shared_memory{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
new file mode 100644
index 000000000..898572277
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -0,0 +1,43 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/common_types.h"
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/gesture.h"
+
+namespace Service::HID {
+constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
+
+Controller_Gesture::Controller_Gesture() = default;
+Controller_Gesture::~Controller_Gesture() = default;
+
+void Controller_Gesture::OnInit() {}
+
+void Controller_Gesture::OnRelease() {}
+
+void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
+ shared_memory.header.timestamp = CoreTiming::GetTicks();
+ shared_memory.header.total_entry_count = 17;
+
+ if (!IsControllerActivated()) {
+ shared_memory.header.entry_count = 0;
+ shared_memory.header.last_entry_index = 0;
+ return;
+ }
+ shared_memory.header.entry_count = 16;
+
+ const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+ auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+
+ cur_entry.sampling_number = last_entry.sampling_number + 1;
+ cur_entry.sampling_number2 = cur_entry.sampling_number;
+ // TODO(ogniK): Update gesture states
+
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
+}
+
+void Controller_Gesture::OnLoadInputDevices() {}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
new file mode 100644
index 000000000..1056ffbcd
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -0,0 +1,63 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+class Controller_Gesture final : public ControllerBase {
+public:
+ Controller_Gesture();
+ ~Controller_Gesture() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+private:
+ struct Locations {
+ s32_le x;
+ s32_le y;
+ };
+
+ struct GestureState {
+ s64_le sampling_number;
+ s64_le sampling_number2;
+
+ s64_le detection_count;
+ s32_le type;
+ s32_le dir;
+ s32_le x;
+ s32_le y;
+ s32_le delta_x;
+ s32_le delta_y;
+ f32 vel_x;
+ f32 vel_y;
+ s32_le attributes;
+ f32 scale;
+ f32 rotation;
+ s32_le location_count;
+ std::array<Locations, 4> locations;
+ };
+ static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
+
+ struct SharedMemory {
+ CommonHeader header;
+ std::array<GestureState, 17> gesture_states;
+ };
+ SharedMemory shared_memory{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
new file mode 100644
index 000000000..ccfbce9ac
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -0,0 +1,43 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/common_types.h"
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/keyboard.h"
+
+namespace Service::HID {
+constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
+
+Controller_Keyboard::Controller_Keyboard() = default;
+Controller_Keyboard::~Controller_Keyboard() = default;
+
+void Controller_Keyboard::OnInit() {}
+
+void Controller_Keyboard::OnRelease() {}
+
+void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
+ shared_memory.header.timestamp = CoreTiming::GetTicks();
+ shared_memory.header.total_entry_count = 17;
+
+ if (!IsControllerActivated()) {
+ shared_memory.header.entry_count = 0;
+ shared_memory.header.last_entry_index = 0;
+ return;
+ }
+ shared_memory.header.entry_count = 16;
+
+ const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
+ shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+ auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
+
+ cur_entry.sampling_number = last_entry.sampling_number + 1;
+ cur_entry.sampling_number2 = cur_entry.sampling_number;
+ // TODO(ogniK): Update keyboard states
+
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
+}
+
+void Controller_Keyboard::OnLoadInputDevices() {}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
new file mode 100644
index 000000000..493e68fce
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+class Controller_Keyboard final : public ControllerBase {
+public:
+ Controller_Keyboard();
+ ~Controller_Keyboard() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, std::size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+private:
+ struct KeyboardState {
+ s64_le sampling_number;
+ s64_le sampling_number2;
+
+ s32_le modifier;
+ s32_le attribute;
+ std::array<u8, 32> key;
+ };
+ static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
+
+ struct SharedMemory {
+ CommonHeader header;
+ std::array<KeyboardState, 17> pad_states;
+ INSERT_PADDING_BYTES(0x28);
+ };
+ static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
+ SharedMemory shared_memory{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
new file mode 100644
index 000000000..4e246a57d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -0,0 +1,43 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/common_types.h"
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/mouse.h"
+
+namespace Service::HID {
+constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
+
+Controller_Mouse::Controller_Mouse() = default;
+Controller_Mouse::~Controller_Mouse() = default;
+
+void Controller_Mouse::OnInit() {}
+
+void Controller_Mouse::OnRelease() {}
+
+void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
+ shared_memory.header.timestamp = CoreTiming::GetTicks();
+ shared_memory.header.total_entry_count = 17;
+
+ if (!IsControllerActivated()) {
+ shared_memory.header.entry_count = 0;
+ shared_memory.header.last_entry_index = 0;
+ return;
+ }
+ shared_memory.header.entry_count = 16;
+
+ auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
+ shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+ auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
+
+ cur_entry.sampling_number = last_entry.sampling_number + 1;
+ cur_entry.sampling_number2 = cur_entry.sampling_number;
+ // TODO(ogniK): Update mouse states
+
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
+}
+
+void Controller_Mouse::OnLoadInputDevices() {}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
new file mode 100644
index 000000000..543b0b71f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+class Controller_Mouse final : public ControllerBase {
+public:
+ Controller_Mouse();
+ ~Controller_Mouse() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, std::size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+private:
+ struct MouseState {
+ s64_le sampling_number;
+ s64_le sampling_number2;
+ s32_le x;
+ s32_le y;
+ s32_le delta_x;
+ s32_le delta_y;
+ s32_le mouse_wheel;
+ s32_le button;
+ s32_le attribute;
+ };
+ static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
+
+ struct SharedMemory {
+ CommonHeader header;
+ std::array<MouseState, 17> mouse_states;
+ };
+ SharedMemory shared_memory{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
new file mode 100644
index 000000000..b26593b4f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -0,0 +1,436 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include "common/assert.h"
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/frontend/input.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/settings.h"
+
+namespace Service::HID {
+
+constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
+constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
+constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
+constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
+constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
+constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+constexpr std::size_t NPAD_OFFSET = 0x9A00;
+constexpr u32 BATTERY_FULL = 2;
+
+constexpr std::array<u32, 10> npad_id_list{
+ 0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
+};
+
+enum class JoystickId : std::size_t {
+ Joystick_Left,
+ Joystick_Right,
+};
+
+Controller_NPad::Controller_NPad() = default;
+Controller_NPad::~Controller_NPad() = default;
+
+void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
+ const auto controller_type = connected_controllers[controller_idx].type;
+ auto& controller = shared_memory_entries[controller_idx];
+ if (controller_type == NPadControllerType::None) {
+ return;
+ }
+ controller.joy_styles.raw = 0; // Zero out
+ controller.device_type.raw = 0;
+ switch (controller_type) {
+ case NPadControllerType::Handheld:
+ controller.joy_styles.handheld.Assign(1);
+ controller.device_type.handheld.Assign(1);
+ controller.pad_assignment = NPadAssignments::Dual;
+ break;
+ case NPadControllerType::JoyDual:
+ controller.joy_styles.joycon_dual.Assign(1);
+ controller.device_type.joycon_left.Assign(1);
+ controller.device_type.joycon_right.Assign(1);
+ controller.pad_assignment = NPadAssignments::Dual;
+ break;
+ case NPadControllerType::JoyLeft:
+ controller.joy_styles.joycon_left.Assign(1);
+ controller.device_type.joycon_left.Assign(1);
+ controller.pad_assignment = NPadAssignments::Dual;
+ break;
+ case NPadControllerType::JoyRight:
+ controller.joy_styles.joycon_right.Assign(1);
+ controller.device_type.joycon_right.Assign(1);
+ controller.pad_assignment = NPadAssignments::Dual;
+ break;
+ case NPadControllerType::Pokeball:
+ controller.joy_styles.pokeball.Assign(1);
+ controller.device_type.pokeball.Assign(1);
+ controller.pad_assignment = NPadAssignments::Single;
+ break;
+ case NPadControllerType::ProController:
+ controller.joy_styles.pro_controller.Assign(1);
+ controller.device_type.pro_controller.Assign(1);
+ controller.pad_assignment = NPadAssignments::Single;
+ break;
+ }
+
+ controller.single_color_error = ColorReadError::ReadOk;
+ controller.single_color.body_color = 0;
+ controller.single_color.button_color = 0;
+
+ controller.dual_color_error = ColorReadError::ReadOk;
+ controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
+ controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
+ controller.right_color.body_color = JOYCON_BODY_NEON_RED;
+ controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
+
+ controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
+ controller.battery_level[0] = BATTERY_FULL;
+ controller.battery_level[1] = BATTERY_FULL;
+ controller.battery_level[2] = BATTERY_FULL;
+}
+
+void Controller_NPad::OnInit() {
+ auto& kernel = Core::System::GetInstance().Kernel();
+ styleset_changed_event =
+ Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
+
+ if (!IsControllerActivated())
+ return;
+ std::size_t controller{};
+ if (style.raw == 0) {
+ // We want to support all controllers
+ style.handheld.Assign(1);
+ style.joycon_left.Assign(1);
+ style.joycon_right.Assign(1);
+ style.joycon_dual.Assign(1);
+ style.pro_controller.Assign(1);
+ style.pokeball.Assign(1);
+ }
+ if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
+ [](const ControllerHolder& controller) { return controller.is_connected; })) {
+ supported_npad_id_types.resize(npad_id_list.size());
+ std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
+ npad_id_list.size() * sizeof(u32));
+ AddNewController(NPadControllerType::JoyDual);
+ }
+}
+
+void Controller_NPad::OnLoadInputDevices() {
+ std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+ Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
+ buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
+ std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
+ Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
+ sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
+}
+
+void Controller_NPad::OnRelease() {}
+
+void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
+ if (!IsControllerActivated())
+ return;
+ for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
+ auto& npad = shared_memory_entries[i];
+ const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
+ &npad.handheld_states,
+ &npad.dual_states,
+ &npad.left_joy_states,
+ &npad.right_joy_states,
+ &npad.pokeball_states,
+ &npad.libnx};
+
+ for (auto* main_controller : controller_npads) {
+ main_controller->common.entry_count = 16;
+ main_controller->common.total_entry_count = 17;
+
+ const auto& last_entry =
+ main_controller->npad[main_controller->common.last_entry_index];
+
+ main_controller->common.timestamp = CoreTiming::GetTicks();
+ main_controller->common.last_entry_index =
+ (main_controller->common.last_entry_index + 1) % 17;
+
+ auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
+
+ cur_entry.timestamp = last_entry.timestamp + 1;
+ cur_entry.timestamp2 = cur_entry.timestamp;
+ }
+
+ const auto& controller_type = connected_controllers[i].type;
+
+ if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
+ continue;
+ }
+
+ // Pad states
+ ControllerPadState pad_state{};
+ using namespace Settings::NativeButton;
+ pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
+
+ AnalogPosition lstick_entry{};
+ AnalogPosition rstick_entry{};
+
+ const auto [stick_l_x_f, stick_l_y_f] =
+ sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+ const auto [stick_r_x_f, stick_r_y_f] =
+ sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
+ lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+ lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+ rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+ rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+
+ auto& main_controller =
+ npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
+ auto& handheld_entry =
+ npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
+ auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
+ auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
+ auto& right_entry =
+ npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
+ auto& pokeball_entry =
+ npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
+ auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
+
+ if (hold_type == NpadHoldType::Horizontal) {
+ // TODO(ogniK): Remap buttons for different orientations
+ }
+ libnx_entry.connection_status.raw = 0;
+
+ switch (controller_type) {
+ case NPadControllerType::Handheld:
+ handheld_entry.connection_status.raw = 0;
+ handheld_entry.connection_status.IsConnected.Assign(1);
+ if (!Settings::values.use_docked_mode) {
+ handheld_entry.connection_status.IsWired.Assign(1);
+ }
+ handheld_entry.pad_states.raw = pad_state.raw;
+ handheld_entry.l_stick = lstick_entry;
+ handheld_entry.r_stick = rstick_entry;
+ break;
+ case NPadControllerType::JoyDual:
+ dual_entry.connection_status.raw = 0;
+
+ dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ dual_entry.connection_status.IsRightJoyConnected.Assign(1);
+ dual_entry.connection_status.IsConnected.Assign(1);
+
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsConnected.Assign(1);
+
+ dual_entry.pad_states.raw = pad_state.raw;
+ dual_entry.l_stick = lstick_entry;
+ dual_entry.r_stick = rstick_entry;
+ case NPadControllerType::JoyLeft:
+ left_entry.connection_status.raw = 0;
+
+ left_entry.connection_status.IsConnected.Assign(1);
+ left_entry.pad_states.raw = pad_state.raw;
+ left_entry.l_stick = lstick_entry;
+ left_entry.r_stick = rstick_entry;
+ break;
+ case NPadControllerType::JoyRight:
+ right_entry.connection_status.raw = 0;
+
+ right_entry.connection_status.IsConnected.Assign(1);
+ right_entry.pad_states.raw = pad_state.raw;
+ right_entry.l_stick = lstick_entry;
+ right_entry.r_stick = rstick_entry;
+ break;
+ case NPadControllerType::Pokeball:
+ pokeball_entry.connection_status.raw = 0;
+
+ pokeball_entry.connection_status.IsConnected.Assign(1);
+ pokeball_entry.connection_status.IsWired.Assign(1);
+
+ pokeball_entry.pad_states.raw = pad_state.raw;
+ pokeball_entry.l_stick = lstick_entry;
+ pokeball_entry.r_stick = rstick_entry;
+ break;
+ case NPadControllerType::ProController:
+ main_controller.connection_status.raw = 0;
+
+ main_controller.connection_status.IsConnected.Assign(1);
+ main_controller.connection_status.IsWired.Assign(1);
+ main_controller.pad_states.raw = pad_state.raw;
+ main_controller.l_stick = lstick_entry;
+ main_controller.r_stick = rstick_entry;
+ break;
+ }
+
+ // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
+ // any controllers.
+ libnx_entry.pad_states.raw = pad_state.raw;
+ libnx_entry.l_stick = lstick_entry;
+ libnx_entry.r_stick = rstick_entry;
+ }
+ std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
+ shared_memory_entries.size() * sizeof(NPadEntry));
+} // namespace Service::HID
+
+void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
+ style.raw = style_set.raw;
+}
+
+Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
+ return style;
+}
+
+void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
+ ASSERT(length > 0 && (length % sizeof(u32)) == 0);
+ supported_npad_id_types.clear();
+ supported_npad_id_types.resize(length / sizeof(u32));
+ std::memcpy(supported_npad_id_types.data(), data, length);
+}
+
+void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
+ ASSERT(max_length < supported_npad_id_types.size());
+ std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
+}
+
+std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
+ return supported_npad_id_types.size();
+}
+
+void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
+ hold_type = joy_hold_type;
+}
+Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
+ return hold_type;
+}
+
+void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
+ ASSERT(npad_id < shared_memory_entries.size());
+ shared_memory_entries[npad_id].pad_assignment = assignment_mode;
+}
+
+void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
+ const std::vector<Vibration>& vibrations) {
+ if (!can_controllers_vibrate) {
+ return;
+ }
+ for (std::size_t i = 0; i < controller_ids.size(); i++) {
+ std::size_t controller_pos = i;
+ // Handheld controller conversion
+ if (controller_pos == 32) {
+ controller_pos = 8;
+ }
+ // Unknown controller conversion
+ if (controller_pos == 16) {
+ controller_pos = 9;
+ }
+ if (connected_controllers[controller_pos].is_connected) {
+ // TODO(ogniK): Vibrate the physical controller
+ }
+ }
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ last_processed_vibration = vibrations.back();
+}
+
+Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
+ return styleset_changed_event;
+}
+
+Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
+ return last_processed_vibration;
+}
+void Controller_NPad::AddNewController(NPadControllerType controller) {
+ if (controller == NPadControllerType::Handheld) {
+ connected_controllers[8] = {controller, true};
+ InitNewlyAddedControler(8);
+ return;
+ }
+ const auto pos =
+ std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
+ [](const ControllerHolder& holder) { return !holder.is_connected; });
+ if (pos == connected_controllers.end() - 2) {
+ LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
+ return;
+ }
+ const auto controller_id = std::distance(connected_controllers.begin(), pos);
+ connected_controllers[controller_id] = {controller, true};
+ InitNewlyAddedControler(controller_id);
+}
+
+void Controller_NPad::ConnectNPad(u32 npad_id) {
+ if (npad_id >= connected_controllers.size())
+ return;
+ connected_controllers[npad_id].is_connected = true;
+}
+
+void Controller_NPad::DisconnectNPad(u32 npad_id) {
+ if (npad_id >= connected_controllers.size())
+ return;
+ connected_controllers[npad_id].is_connected = false;
+}
+
+Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
+ if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
+ // These are controllers without led patterns
+ return LedPattern{0, 0, 0, 0};
+ }
+ switch (npad_id) {
+ case 0:
+ return LedPattern{1, 0, 0, 0};
+ case 1:
+ return LedPattern{0, 1, 0, 0};
+ case 2:
+ return LedPattern{0, 0, 1, 0};
+ case 3:
+ return LedPattern{0, 0, 0, 1};
+ case 4:
+ return LedPattern{1, 0, 0, 1};
+ case 5:
+ return LedPattern{1, 0, 1, 0};
+ case 6:
+ return LedPattern{1, 0, 1, 1};
+ case 7:
+ return LedPattern{0, 1, 1, 0};
+ default:
+ UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
+ return LedPattern{0, 0, 0, 0};
+ };
+}
+void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
+ can_controllers_vibrate = can_vibrate;
+}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
new file mode 100644
index 000000000..7c0f93acf
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -0,0 +1,287 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+#include "core/frontend/input.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/settings.h"
+
+namespace Service::HID {
+
+class Controller_NPad final : public ControllerBase {
+public:
+ Controller_NPad();
+ ~Controller_NPad() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, std::size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+ struct NPadType {
+ union {
+ u32_le raw{};
+
+ BitField<0, 1, u32_le> pro_controller;
+ BitField<1, 1, u32_le> handheld;
+ BitField<2, 1, u32_le> joycon_dual;
+ BitField<3, 1, u32_le> joycon_left;
+ BitField<4, 1, u32_le> joycon_right;
+
+ BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
+ };
+ };
+ static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
+
+ struct Vibration {
+ f32 amp_low;
+ f32 freq_low;
+ f32 amp_high;
+ f32 freq_high;
+ };
+ static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
+
+ enum class NpadHoldType : u64 {
+ Vertical = 0,
+ Horizontal = 1,
+ };
+
+ enum class NPadAssignments : u32_le {
+ Dual = 0,
+ Single = 1,
+ };
+
+ enum class NPadControllerType {
+ None,
+ ProController,
+ Handheld,
+ JoyDual,
+ JoyLeft,
+ JoyRight,
+ Pokeball,
+ };
+
+ struct LedPattern {
+ explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
+ position1.Assign(light1);
+ position1.Assign(light2);
+ position1.Assign(light3);
+ position1.Assign(light4);
+ }
+ union {
+ u64 raw{};
+ BitField<0, 1, u64> position1;
+ BitField<1, 1, u64> position2;
+ BitField<2, 1, u64> position3;
+ BitField<3, 1, u64> position4;
+ };
+ };
+
+ void SetSupportedStyleSet(NPadType style_set);
+ NPadType GetSupportedStyleSet() const;
+
+ void SetSupportedNPadIdTypes(u8* data, std::size_t length);
+ void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
+ std::size_t GetSupportedNPadIdTypesSize() const;
+
+ void SetHoldType(NpadHoldType joy_hold_type);
+ NpadHoldType GetHoldType() const;
+
+ void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
+
+ void VibrateController(const std::vector<u32>& controller_ids,
+ const std::vector<Vibration>& vibrations);
+
+ Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
+ Vibration GetLastVibration() const;
+
+ void AddNewController(NPadControllerType controller);
+
+ void ConnectNPad(u32 npad_id);
+ void DisconnectNPad(u32 npad_id);
+ LedPattern GetLedPattern(u32 npad_id);
+ void SetVibrationEnabled(bool can_vibrate);
+
+private:
+ struct CommonHeader {
+ s64_le timestamp;
+ s64_le total_entry_count;
+ s64_le last_entry_index;
+ s64_le entry_count;
+ };
+ static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+
+ struct ControllerColor {
+ u32_le body_color;
+ u32_le button_color;
+ };
+ static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
+
+ struct ControllerPadState {
+ union {
+ u64_le raw{};
+ // Button states
+ BitField<0, 1, u64_le> a;
+ BitField<1, 1, u64_le> b;
+ BitField<2, 1, u64_le> x;
+ BitField<3, 1, u64_le> y;
+ BitField<4, 1, u64_le> l_stick;
+ BitField<5, 1, u64_le> r_stick;
+ BitField<6, 1, u64_le> l;
+ BitField<7, 1, u64_le> r;
+ BitField<8, 1, u64_le> zl;
+ BitField<9, 1, u64_le> zr;
+ BitField<10, 1, u64_le> plus;
+ BitField<11, 1, u64_le> minus;
+
+ // D-Pad
+ BitField<12, 1, u64_le> d_left;
+ BitField<13, 1, u64_le> d_up;
+ BitField<14, 1, u64_le> d_right;
+ BitField<15, 1, u64_le> d_down;
+
+ // Left JoyStick
+ BitField<16, 1, u64_le> l_stick_left;
+ BitField<17, 1, u64_le> l_stick_up;
+ BitField<18, 1, u64_le> l_stick_right;
+ BitField<19, 1, u64_le> l_stick_down;
+
+ // Right JoyStick
+ BitField<20, 1, u64_le> r_stick_left;
+ BitField<21, 1, u64_le> r_stick_up;
+ BitField<22, 1, u64_le> r_stick_right;
+ BitField<23, 1, u64_le> r_stick_down;
+
+ // Not always active?
+ BitField<24, 1, u64_le> sl;
+ BitField<25, 1, u64_le> sr;
+ };
+ };
+ static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
+
+ struct AnalogPosition {
+ s32_le x;
+ s32_le y;
+ };
+ static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
+
+ struct ConnectionState {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32_le> IsConnected;
+ BitField<1, 1, u32_le> IsWired;
+ BitField<2, 1, u32_le> IsLeftJoyConnected;
+ BitField<3, 1, u32_le> IsLeftJoyWired;
+ BitField<4, 1, u32_le> IsRightJoyConnected;
+ BitField<5, 1, u32_le> IsRightJoyWired;
+ };
+ };
+ static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
+
+ struct GenericStates {
+ s64_le timestamp;
+ s64_le timestamp2;
+ ControllerPadState pad_states;
+ AnalogPosition l_stick;
+ AnalogPosition r_stick;
+ ConnectionState connection_status;
+ };
+ static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
+
+ struct NPadGeneric {
+ CommonHeader common;
+ std::array<GenericStates, 17> npad;
+ };
+ static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
+
+ enum class ColorReadError : u32_le {
+ ReadOk = 0,
+ ColorDoesntExist = 1,
+ NoController = 2,
+ };
+
+ struct NPadProperties {
+ union {
+ s64_le raw{};
+ BitField<11, 1, s64_le> is_vertical;
+ BitField<12, 1, s64_le> is_horizontal;
+ BitField<13, 1, s64_le> use_plus;
+ BitField<14, 1, s64_le> use_minus;
+ };
+ };
+
+ struct NPadDevice {
+ union {
+ u32_le raw{};
+ BitField<0, 1, s32_le> pro_controller;
+ BitField<1, 1, s32_le> handheld;
+ BitField<2, 1, s32_le> handheld_left;
+ BitField<3, 1, s32_le> handheld_right;
+ BitField<4, 1, s32_le> joycon_left;
+ BitField<5, 1, s32_le> joycon_right;
+ BitField<6, 1, s32_le> pokeball;
+ };
+ };
+
+ struct NPadEntry {
+ NPadType joy_styles;
+ NPadAssignments pad_assignment;
+
+ ColorReadError single_color_error;
+ ControllerColor single_color;
+
+ ColorReadError dual_color_error;
+ ControllerColor left_color;
+ ControllerColor right_color;
+
+ NPadGeneric main_controller_states;
+ NPadGeneric handheld_states;
+ NPadGeneric dual_states;
+ NPadGeneric left_joy_states;
+ NPadGeneric right_joy_states;
+ NPadGeneric pokeball_states;
+ NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
+ // relying on this for the time being
+ INSERT_PADDING_BYTES(
+ 0x708 *
+ 6); // TODO(ogniK): SixAxis states, require more information before implementation
+ NPadDevice device_type;
+ NPadProperties properties;
+ INSERT_PADDING_WORDS(1);
+ std::array<u32, 3> battery_level;
+ INSERT_PADDING_BYTES(0x5c);
+ INSERT_PADDING_BYTES(0xdf8);
+ };
+ static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
+
+ struct ControllerHolder {
+ Controller_NPad::NPadControllerType type;
+ bool is_connected;
+ };
+
+ NPadType style{};
+ std::array<NPadEntry, 10> shared_memory_entries{};
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
+ buttons;
+ std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
+ std::vector<u32> supported_npad_id_types{};
+ NpadHoldType hold_type{NpadHoldType::Vertical};
+ Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
+ Vibration last_processed_vibration{};
+ std::array<ControllerHolder, 10> connected_controllers{};
+ bool can_controllers_vibrate{true};
+
+ void InitNewlyAddedControler(std::size_t controller_idx);
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
new file mode 100644
index 000000000..02fcfadd9
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -0,0 +1,39 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/common_types.h"
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/stubbed.h"
+
+namespace Service::HID {
+
+Controller_Stubbed::Controller_Stubbed() = default;
+Controller_Stubbed::~Controller_Stubbed() = default;
+
+void Controller_Stubbed::OnInit() {}
+
+void Controller_Stubbed::OnRelease() {}
+
+void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
+ if (!smart_update) {
+ return;
+ }
+
+ CommonHeader header{};
+ header.timestamp = CoreTiming::GetTicks();
+ header.total_entry_count = 17;
+ header.entry_count = 0;
+ header.last_entry_index = 0;
+
+ std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
+}
+
+void Controller_Stubbed::OnLoadInputDevices() {}
+
+void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
+ common_offset = off;
+ smart_update = true;
+}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
new file mode 100644
index 000000000..4a21c643e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -0,0 +1,34 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+class Controller_Stubbed final : public ControllerBase {
+public:
+ Controller_Stubbed();
+ ~Controller_Stubbed() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, std::size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+ void SetCommonHeaderOffset(std::size_t off);
+
+private:
+ bool smart_update{};
+ std::size_t common_offset{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
new file mode 100644
index 000000000..43efef803
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -0,0 +1,65 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/common_types.h"
+#include "core/core_timing.h"
+#include "core/frontend/emu_window.h"
+#include "core/frontend/input.h"
+#include "core/hle/service/hid/controllers/touchscreen.h"
+#include "core/settings.h"
+
+namespace Service::HID {
+constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
+
+Controller_Touchscreen::Controller_Touchscreen() = default;
+Controller_Touchscreen::~Controller_Touchscreen() = default;
+
+void Controller_Touchscreen::OnInit() {}
+
+void Controller_Touchscreen::OnRelease() {}
+
+void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
+ shared_memory.header.timestamp = CoreTiming::GetTicks();
+ shared_memory.header.total_entry_count = 17;
+
+ if (!IsControllerActivated()) {
+ shared_memory.header.entry_count = 0;
+ shared_memory.header.last_entry_index = 0;
+ return;
+ }
+ shared_memory.header.entry_count = 16;
+
+ const auto& last_entry =
+ shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
+ shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+ auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
+
+ cur_entry.sampling_number = last_entry.sampling_number + 1;
+ cur_entry.sampling_number2 = cur_entry.sampling_number;
+
+ const auto [x, y, pressed] = touch_device->GetStatus();
+ auto& touch_entry = cur_entry.states[0];
+ if (pressed) {
+ touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
+ touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
+ touch_entry.diameter_x = 15;
+ touch_entry.diameter_y = 15;
+ touch_entry.rotation_angle = 0;
+ const u64 tick = CoreTiming::GetTicks();
+ touch_entry.delta_time = tick - last_touch;
+ last_touch = tick;
+ touch_entry.finger = 0;
+ cur_entry.entry_count = 1;
+ } else {
+ cur_entry.entry_count = 0;
+ }
+
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
+}
+
+void Controller_Touchscreen::OnLoadInputDevices() {
+ touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
+}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
new file mode 100644
index 000000000..e5db6e6ba
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -0,0 +1,63 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/frontend/input.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+class Controller_Touchscreen final : public ControllerBase {
+public:
+ Controller_Touchscreen();
+ ~Controller_Touchscreen() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, std::size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+private:
+ struct TouchState {
+ u64_le delta_time;
+ u32_le attribute;
+ u32_le finger;
+ u32_le x;
+ u32_le y;
+ u32_le diameter_x;
+ u32_le diameter_y;
+ u32_le rotation_angle;
+ };
+ static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+
+ struct TouchScreenEntry {
+ s64_le sampling_number;
+ s64_le sampling_number2;
+ s32_le entry_count;
+ std::array<TouchState, 16> states;
+ };
+ static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
+
+ struct TouchScreenSharedMemory {
+ CommonHeader header;
+ std::array<TouchScreenEntry, 17> shared_memory_entries{};
+ INSERT_PADDING_BYTES(0x3c8);
+ };
+ static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
+ "TouchScreenSharedMemory is an invalid size");
+ TouchScreenSharedMemory shared_memory{};
+ std::unique_ptr<Input::TouchDevice> touch_device;
+ s64_le last_touch{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
new file mode 100644
index 000000000..cd397c70b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -0,0 +1,45 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/common_types.h"
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/xpad.h"
+
+namespace Service::HID {
+constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
+
+Controller_XPad::Controller_XPad() = default;
+Controller_XPad::~Controller_XPad() = default;
+
+void Controller_XPad::OnInit() {}
+
+void Controller_XPad::OnRelease() {}
+
+void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
+ for (auto& xpad_entry : shared_memory.shared_memory_entries) {
+ xpad_entry.header.timestamp = CoreTiming::GetTicks();
+ xpad_entry.header.total_entry_count = 17;
+
+ if (!IsControllerActivated()) {
+ xpad_entry.header.entry_count = 0;
+ xpad_entry.header.last_entry_index = 0;
+ return;
+ }
+ xpad_entry.header.entry_count = 16;
+
+ const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
+ xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
+ auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
+
+ cur_entry.sampling_number = last_entry.sampling_number + 1;
+ cur_entry.sampling_number2 = cur_entry.sampling_number;
+ }
+ // TODO(ogniK): Update xpad states
+
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
+}
+
+void Controller_XPad::OnLoadInputDevices() {}
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
new file mode 100644
index 000000000..ff836989f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -0,0 +1,60 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+class Controller_XPad final : public ControllerBase {
+public:
+ Controller_XPad();
+ ~Controller_XPad() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(u8* data, std::size_t size) override;
+
+ // Called when input devices should be loaded
+ void OnLoadInputDevices() override;
+
+private:
+ struct AnalogStick {
+ s32_le x;
+ s32_le y;
+ };
+ static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
+
+ struct XPadState {
+ s64_le sampling_number;
+ s64_le sampling_number2;
+ s32_le attributes;
+ u32_le pad_states;
+ AnalogStick x_stick;
+ AnalogStick y_stick;
+ };
+ static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
+
+ struct XPadEntry {
+ CommonHeader header;
+ std::array<XPadState, 17> pad_states{};
+ INSERT_PADDING_BYTES(0x138);
+ };
+ static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
+
+ struct SharedMemory {
+ std::array<XPadEntry, 4> shared_memory_entries{};
+ };
+ static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
+ SharedMemory shared_memory{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 7c6b0a4e6..beb89218a 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -19,6 +21,16 @@
#include "core/hle/service/service.h"
#include "core/settings.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/controllers/debug_pad.h"
+#include "core/hle/service/hid/controllers/gesture.h"
+#include "core/hle/service/hid/controllers/keyboard.h"
+#include "core/hle/service/hid/controllers/mouse.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/controllers/stubbed.h"
+#include "core/hle/service/hid/controllers/touchscreen.h"
+#include "core/hle/service/hid/controllers/xpad.h"
+
namespace Service::HID {
// Updating period for each HID device.
@@ -26,6 +38,22 @@ namespace Service::HID {
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
+constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
+enum class HidController : std::size_t {
+ DebugPad,
+ Touchscreen,
+ Mouse,
+ Keyboard,
+ XPad,
+ Unknown1,
+ Unknown2,
+ Unknown3,
+ SixAxisSensor,
+ NPad,
+ Gesture,
+
+ MaxControllers,
+};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
@@ -37,19 +65,57 @@ public:
auto& kernel = Core::System::GetInstance().Kernel();
shared_mem = Kernel::SharedMemory::Create(
- kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite,
+ kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
+ MakeController<Controller_DebugPad>(HidController::DebugPad);
+ MakeController<Controller_Touchscreen>(HidController::Touchscreen);
+ MakeController<Controller_Mouse>(HidController::Mouse);
+ MakeController<Controller_Keyboard>(HidController::Keyboard);
+ MakeController<Controller_XPad>(HidController::XPad);
+ MakeController<Controller_Stubbed>(HidController::Unknown1);
+ MakeController<Controller_Stubbed>(HidController::Unknown2);
+ MakeController<Controller_Stubbed>(HidController::Unknown3);
+ MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
+ MakeController<Controller_NPad>(HidController::NPad);
+ MakeController<Controller_Gesture>(HidController::Gesture);
+
+ // Homebrew doesn't try to activate some controllers, so we activate them by default
+ GetController<Controller_NPad>(HidController::NPad).ActivateController();
+ GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
+
+ GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
+ GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
+ GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
+
// Register update callbacks
pad_update_event = CoreTiming::RegisterEvent(
"HID::UpdatePadCallback",
- [this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); });
+ [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
}
+ void ActivateController(HidController controller) {
+ controllers[static_cast<size_t>(controller)]->ActivateController();
+ }
+
+ void DeactivateController(HidController controller) {
+ controllers[static_cast<size_t>(controller)]->DeactivateController();
+ }
+
+ template <typename T>
+ void MakeController(HidController controller) {
+ controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
+ }
+
+ template <typename T>
+ T& GetController(HidController controller) {
+ return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
+ }
+
~IAppletResource() {
CoreTiming::UnscheduleEvent(pad_update_event, 0);
}
@@ -62,200 +128,15 @@ private:
LOG_DEBUG(Service_HID, "called");
}
- void LoadInputDevices() {
- std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
- Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
- buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
- std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
- Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
- sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
- touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
- // TODO(shinyquagsire23): gyro, mouse, keyboard
- }
-
- void UpdatePadCallback(u64 userdata, int cycles_late) {
- SharedMemory mem{};
- std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
-
- if (Settings::values.is_device_reload_pending.exchange(false))
- LoadInputDevices();
-
- // Set up controllers as neon red+blue Joy-Con attached to console
- ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
- controller_header.type = ControllerType_Handheld;
- controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
- controller_header.right_color_body = JOYCON_BODY_NEON_RED;
- controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
- controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
- controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
-
- for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) {
- for (auto& layout : mem.controllers[controller].layouts) {
- layout.header.num_entries = HID_NUM_ENTRIES;
- layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
-
- // HID shared memory stores the state of the past 17 samples in a circlular buffer,
- // each with a timestamp in number of samples since boot.
- const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
-
- layout.header.timestamp_ticks = CoreTiming::GetTicks();
- layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
-
- ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
- entry.timestamp = last_entry.timestamp + 1;
- // TODO(shinyquagsire23): Is this always identical to timestamp?
- entry.timestamp_2 = entry.timestamp;
-
- // TODO(shinyquagsire23): More than just handheld input
- if (controller != Controller_Handheld)
- continue;
-
- entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
-
- // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
- // For now everything is just the default handheld layout, but split Joy-Con will
- // rotate the face buttons and directions for certain layouts.
- ControllerPadState& state = entry.buttons;
- using namespace Settings::NativeButton;
- state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
- state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
- state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
- state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
- state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
- state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
- state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
- state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
- state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
- state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
- state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
- state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
-
- state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
- state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
- state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
-
- state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
- state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
- state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
- state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
-
- state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
- state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
- state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
- state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
-
- state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
- state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
-
- const auto [stick_l_x_f, stick_l_y_f] = sticks[Joystick_Left]->GetStatus();
- const auto [stick_r_x_f, stick_r_y_f] = sticks[Joystick_Right]->GetStatus();
- entry.joystick_left_x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
- entry.joystick_left_y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
- entry.joystick_right_x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
- entry.joystick_right_y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+ void UpdateControllers(u64 userdata, int cycles_late) {
+ const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
+ for (const auto& controller : controllers) {
+ if (should_reload) {
+ controller->OnLoadInputDevices();
}
+ controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
- TouchScreen& touchscreen = mem.touchscreen;
- const u64 last_entry = touchscreen.header.latest_entry;
- const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
- const u64 timestamp = CoreTiming::GetTicks();
- const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
- touchscreen.header.timestamp_ticks = timestamp;
- touchscreen.header.num_entries = touchscreen.entries.size();
- touchscreen.header.latest_entry = curr_entry;
- touchscreen.header.max_entry_index = touchscreen.entries.size();
- touchscreen.header.timestamp = timestamp;
- touchscreen.entries[curr_entry].header.timestamp = sample_counter;
-
- TouchScreenEntryTouch touch_entry{};
- auto [x, y, pressed] = touch_device->GetStatus();
- touch_entry.timestamp = timestamp;
- touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
- touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
- touch_entry.touch_index = 0;
-
- // TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
- touch_entry.diameter_x = 15;
- touch_entry.diameter_y = 15;
- touch_entry.angle = 0;
-
- // TODO(DarkLordZach): Implement multi-touch support
- if (pressed) {
- touchscreen.entries[curr_entry].header.num_touches = 1;
- touchscreen.entries[curr_entry].touches[0] = touch_entry;
- } else {
- touchscreen.entries[curr_entry].header.num_touches = 0;
- }
-
- // TODO(shinyquagsire23): Properly implement mouse
- Mouse& mouse = mem.mouse;
- const u64 last_mouse_entry = mouse.header.latest_entry;
- const u64 curr_mouse_entry = (mouse.header.latest_entry + 1) % mouse.entries.size();
- const u64 mouse_sample_counter = mouse.entries[last_mouse_entry].timestamp + 1;
- mouse.header.timestamp_ticks = timestamp;
- mouse.header.num_entries = mouse.entries.size();
- mouse.header.max_entry_index = mouse.entries.size();
- mouse.header.latest_entry = curr_mouse_entry;
-
- mouse.entries[curr_mouse_entry].timestamp = mouse_sample_counter;
- mouse.entries[curr_mouse_entry].timestamp_2 = mouse_sample_counter;
-
- // TODO(shinyquagsire23): Properly implement keyboard
- Keyboard& keyboard = mem.keyboard;
- const u64 last_keyboard_entry = keyboard.header.latest_entry;
- const u64 curr_keyboard_entry =
- (keyboard.header.latest_entry + 1) % keyboard.entries.size();
- const u64 keyboard_sample_counter = keyboard.entries[last_keyboard_entry].timestamp + 1;
- keyboard.header.timestamp_ticks = timestamp;
- keyboard.header.num_entries = keyboard.entries.size();
- keyboard.header.latest_entry = last_keyboard_entry;
- keyboard.header.max_entry_index = keyboard.entries.size();
-
- keyboard.entries[curr_keyboard_entry].timestamp = keyboard_sample_counter;
- keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter;
-
- // TODO(shinyquagsire23): Figure out what any of these are
- for (auto& input : mem.unk_input_1) {
- const u64 last_input_entry = input.header.latest_entry;
- const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
- const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
-
- input.header.timestamp_ticks = timestamp;
- input.header.num_entries = input.entries.size();
- input.header.latest_entry = last_input_entry;
- input.header.max_entry_index = input.entries.size();
-
- input.entries[curr_input_entry].timestamp = input_sample_counter;
- input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
- }
-
- for (auto& input : mem.unk_input_2) {
- input.header.timestamp_ticks = timestamp;
- input.header.num_entries = 17;
- input.header.latest_entry = 0;
- input.header.max_entry_index = 0;
- }
-
- UnkInput3& input = mem.unk_input_3;
- const u64 last_input_entry = input.header.latest_entry;
- const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
- const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
-
- input.header.timestamp_ticks = timestamp;
- input.header.num_entries = input.entries.size();
- input.header.latest_entry = last_input_entry;
- input.header.max_entry_index = input.entries.size();
-
- input.entries[curr_input_entry].timestamp = input_sample_counter;
- input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
-
- // TODO(shinyquagsire23): Signal events
-
- std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
-
- // Reschedule recurrent event
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
}
@@ -265,11 +146,8 @@ private:
// CoreTiming update events
CoreTiming::EventType* pad_update_event;
- // Stored input state info
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
- buttons;
- std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
- std::unique_ptr<Input::TouchDevice> touch_device;
+ std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
+ controllers{};
};
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -299,9 +177,10 @@ public:
{11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
{21, &Hid::ActivateMouse, "ActivateMouse"},
{31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
+ {32, nullptr, "SendKeyboardLockKeyEvent"},
{40, nullptr, "AcquireXpadIdEventHandle"},
{41, nullptr, "ReleaseXpadIdEventHandle"},
- {51, nullptr, "ActivateXpad"},
+ {51, &Hid::ActivateXpad, "ActivateXpad"},
{55, nullptr, "GetXpadIds"},
{56, nullptr, "ActivateJoyXpad"},
{58, nullptr, "GetJoyXpadLifoHandle"},
@@ -329,6 +208,7 @@ public:
{80, nullptr, "GetGyroscopeZeroDriftMode"},
{81, nullptr, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
+ {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -362,8 +242,8 @@ public:
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
{207, nullptr, "SendVibrationGcErmCommand"},
{208, nullptr, "GetActualVibrationGcErmCommand"},
- {209, nullptr, "BeginPermitVibrationSession"},
- {210, nullptr, "EndPermitVibrationSession"},
+ {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
+ {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
{300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
{301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
{302, nullptr, "StopConsoleSixAxisSensor"},
@@ -374,6 +254,7 @@ public:
{307, nullptr, "FinalizeSevenSixAxisSensor"},
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
+ {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
{401, nullptr, "EnableUsbFullKeyController"},
{402, nullptr, "IsUsbFullKeyControllerConnected"},
@@ -389,28 +270,35 @@ public:
{505, nullptr, "SetPalmaFrModeType"},
{506, nullptr, "ReadPalmaStep"},
{507, nullptr, "EnablePalmaStep"},
- {508, nullptr, "SuspendPalmaStep"},
- {509, nullptr, "ResetPalmaStep"},
- {510, nullptr, "ReadPalmaApplicationSection"},
- {511, nullptr, "WritePalmaApplicationSection"},
- {512, nullptr, "ReadPalmaUniqueCode"},
- {513, nullptr, "SetPalmaUniqueCodeInvalid"},
+ {508, nullptr, "ResetPalmaStep"},
+ {509, nullptr, "ReadPalmaApplicationSection"},
+ {510, nullptr, "WritePalmaApplicationSection"},
+ {511, nullptr, "ReadPalmaUniqueCode"},
+ {512, nullptr, "SetPalmaUniqueCodeInvalid"},
+ {513, nullptr, "WritePalmaActivityEntry"},
+ {514, nullptr, "WritePalmaRgbLedPatternEntry"},
+ {515, nullptr, "WritePalmaWaveEntry"},
+ {516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
+ {517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
+ {518, nullptr, "SuspendPalmaFeature"},
+ {519, nullptr, "GetPalmaOperationResult"},
+ {520, nullptr, "ReadPalmaPlayLog"},
+ {521, nullptr, "ResetPalmaPlayLog"},
+ {522, nullptr, "SetIsPalmaAllConnectable"},
+ {523, nullptr, "SetIsPalmaPairedConnectable"},
+ {524, nullptr, "PairPalma"},
+ {525, nullptr, "SetPalmaBoostMode"},
{1000, nullptr, "SetNpadCommunicationMode"},
{1001, nullptr, "GetNpadCommunicationMode"},
};
// clang-format on
RegisterHandlers(functions);
-
- auto& kernel = Core::System::GetInstance().Kernel();
- event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
}
~Hid() = default;
private:
std::shared_ptr<IAppletResource> applet_resource;
- u32 joy_hold_type{0};
- Kernel::SharedPtr<Kernel::Event> event;
void CreateAppletResource(Kernel::HLERequestContext& ctx) {
if (applet_resource == nullptr) {
@@ -423,31 +311,59 @@ private:
LOG_DEBUG(Service_HID, "called");
}
+ void ActivateXpad(Kernel::HLERequestContext& ctx) {
+ applet_resource->ActivateController(HidController::XPad);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_DEBUG(Service_HID, "called");
+ }
+
void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
+ applet_resource->ActivateController(HidController::DebugPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
+ applet_resource->ActivateController(HidController::Touchscreen);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void ActivateMouse(Kernel::HLERequestContext& ctx) {
+ applet_resource->ActivateController(HidController::Mouse);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
+ applet_resource->ActivateController(HidController::Keyboard);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
+ }
+
+ void ActivateGesture(Kernel::HLERequestContext& ctx) {
+ applet_resource->ActivateController(HidController::Gesture);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_DEBUG(Service_HID, "called");
+ }
+
+ void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
+ // Should have no effect with how our npad sets up the data
+ applet_resource->ActivateController(HidController::NPad);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_DEBUG(Service_HID, "called");
}
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto handle = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -468,84 +384,168 @@ private:
}
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto supported_styleset = rp.PopRaw<u32>();
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSupportedStyleSet({supported_styleset});
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ LOG_DEBUG(Service_HID, "called");
}
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ rb.Push<u32>(controller.GetSupportedStyleSet().raw);
+ LOG_DEBUG(Service_HID, "called");
}
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void ActivateNpad(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ applet_resource->ActivateController(HidController::NPad);
+ LOG_DEBUG(Service_HID, "called");
}
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto npad_id = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(event);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetStyleSetChangedEvent());
+ LOG_DEBUG(Service_HID, "called");
}
void DisconnectNpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto npad_id = rp.PopRaw<u32>();
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .DisconnectNPad(npad_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::RequestParser rp{ctx};
+ auto npad_id = rp.PopRaw<u32>();
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetLedPattern(npad_id)
+ .raw);
+ LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ IPC::RequestParser rp{ctx};
+ const auto hold_type = rp.PopRaw<u64>();
+ controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 3};
+ const auto& controller =
+ applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(joy_hold_type);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
+ LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto npad_id = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
+ void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetVibrationEnabled(true);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_DEBUG(Service_HID, "called");
+ }
+
+ void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetVibrationEnabled(false);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_DEBUG(Service_HID, "called");
+ }
+
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto controller_id = rp.PopRaw<u32>();
+ const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .VibrateController({controller_id}, {vibration_values});
+ LOG_DEBUG(Service_HID, "called");
}
- void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
+ void SendVibrationValues(Kernel::HLERequestContext& ctx) {
+ const auto controllers = ctx.ReadBuffer(0);
+ const auto vibrations = ctx.ReadBuffer(1);
+
+ std::vector<u32> controller_list(controllers.size() / sizeof(u32));
+ std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
+ sizeof(Controller_NPad::Vibration));
+
+ std::memcpy(controller_list.data(), controllers.data(), controllers.size());
+ std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
+ std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
+ [](u32 controller_id) { return controller_id - 3; });
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .VibrateController(controller_list, vibration_list);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
+ }
+
+ void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<Controller_NPad::Vibration>(
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetLastVibration());
+ LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_id = rp.PopRaw<u32>();
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called");
}
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -555,6 +555,8 @@ private:
}
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto mode = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -563,8 +565,9 @@ private:
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(0);
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ rb.Push<u32>(1);
+ rb.Push<u32>(0);
+ LOG_DEBUG(Service_HID, "called");
}
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
@@ -574,12 +577,6 @@ private:
LOG_DEBUG(Service_HID, "called");
}
- void SendVibrationValues(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
- }
-
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -597,18 +594,6 @@ private:
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
-
- void ActivateGesture(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
- }
-
- void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
- }
};
class HidDbg final : public ServiceFramework<HidDbg> {
@@ -650,6 +635,7 @@ public:
{140, nullptr, "DeactivateConsoleSixAxisSensor"},
{141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
{142, nullptr, "DeactivateSevenSixAxisSensor"},
+ {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
{201, nullptr, "ActivateFirmwareUpdate"},
{202, nullptr, "DeactivateFirmwareUpdate"},
{203, nullptr, "StartFirmwareUpdate"},
@@ -660,12 +646,23 @@ public:
{208, nullptr, "StartFirmwareUpdateForRevert"},
{209, nullptr, "GetAvailableFirmwareVersionForRevert"},
{210, nullptr, "IsFirmwareUpdatingDevice"},
+ {211, nullptr, "StartFirmwareUpdateIndividual"},
+ {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
+ {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
{221, nullptr, "UpdateControllerColor"},
{222, nullptr, "ConnectUsbPadsAsync"},
{223, nullptr, "DisconnectUsbPadsAsync"},
{224, nullptr, "UpdateDesignInfo"},
{225, nullptr, "GetUniquePadDriverState"},
{226, nullptr, "GetSixAxisSensorDriverStates"},
+ {227, nullptr, "GetRxPacketHistory"},
+ {228, nullptr, "AcquireOperationEventHandle"},
+ {229, nullptr, "ReadSerialFlash"},
+ {230, nullptr, "WriteSerialFlash"},
+ {231, nullptr, "GetOperationResult"},
+ {232, nullptr, "EnableShipmentMode"},
+ {233, nullptr, "ClearPairingInfo"},
+ {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
{301, nullptr, "GetAbstractedPadHandles"},
{302, nullptr, "GetAbstractedPadState"},
{303, nullptr, "GetAbstractedPadsState"},
@@ -673,6 +670,8 @@ public:
{322, nullptr, "UnsetAutoPilotVirtualPadState"},
{323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
{350, nullptr, "AddRegisteredDevice"},
+ {400, nullptr, "DisableExternalMcuOnNxDevice"},
+ {401, nullptr, "DisableRailDeviceFiltering"},
};
// clang-format on
@@ -708,7 +707,9 @@ public:
{307, nullptr, "GetNpadSystemExtStyle"},
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
{309, nullptr, "GetNpadFullKeyGripColor"},
+ {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
{311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
+ {312, nullptr, "SetSupportedNpadStyleSetAll"},
{321, nullptr, "GetUniquePadsFromNpad"},
{322, nullptr, "GetIrSensorState"},
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
@@ -733,6 +734,7 @@ public:
{546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
{547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, nullptr, "GetRegisteredDevices"},
+ {549, nullptr, "GetConnectableRegisteredDevices"},
{700, nullptr, "ActivateUniquePad"},
{702, nullptr, "AcquireUniquePadConnectionEventHandle"},
{703, nullptr, "GetUniquePadIds"},
@@ -761,6 +763,7 @@ public:
{850, nullptr, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"},
+ {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
{900, nullptr, "ActivateInputDetector"},
{901, nullptr, "NotifyInputDetector"},
{1000, nullptr, "InitializeFirmwareUpdate"},
@@ -780,6 +783,12 @@ public:
{1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
{1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
{1100, nullptr, "GetHidbusSystemServiceObject"},
+ {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
+ {1130, nullptr, "InitializeUsbFirmwareUpdate"},
+ {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
+ {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
+ {1133, nullptr, "StartUsbFirmwareUpdate"},
+ {1134, nullptr, "GetUsbFirmwareUpdateState"},
};
// clang-format on
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 88d926808..773035460 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,408 +4,12 @@
#pragma once
-#include <array>
-#include "common/bit_field.h"
-#include "common/common_types.h"
-#include "core/hle/service/service.h"
+namespace SM {
+class ServiceManager;
+}
namespace Service::HID {
-// Begin enums and output structs
-
-constexpr u32 HID_NUM_ENTRIES = 17;
-constexpr u32 HID_NUM_LAYOUTS = 7;
-constexpr s32 HID_JOYSTICK_MAX = 0x8000;
-constexpr s32 HID_JOYSTICK_MIN = -0x8000;
-
-constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
-constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
-constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
-constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
-
-enum ControllerType : u32 {
- ControllerType_ProController = 1 << 0,
- ControllerType_Handheld = 1 << 1,
- ControllerType_JoyconPair = 1 << 2,
- ControllerType_JoyconLeft = 1 << 3,
- ControllerType_JoyconRight = 1 << 4,
-};
-
-enum ControllerLayoutType : u32 {
- Layout_ProController = 0, // Pro Controller or HID gamepad
- Layout_Handheld = 1, // Two Joy-Con docked to rails
- Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation
- Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment
- Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment
- Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only
- Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation
-};
-
-enum ControllerColorDescription {
- ColorDesc_ColorsNonexistent = 1 << 1,
-};
-
-enum ControllerConnectionState {
- ConnectionState_Connected = 1 << 0,
- ConnectionState_Wired = 1 << 1,
-};
-
-enum ControllerJoystick {
- Joystick_Left = 0,
- Joystick_Right = 1,
-};
-
-enum ControllerID {
- Controller_Player1 = 0,
- Controller_Player2 = 1,
- Controller_Player3 = 2,
- Controller_Player4 = 3,
- Controller_Player5 = 4,
- Controller_Player6 = 5,
- Controller_Player7 = 6,
- Controller_Player8 = 7,
- Controller_Handheld = 8,
- Controller_Unknown = 9,
-};
-
-// End enums and output structs
-
-// Begin UnkInput3
-
-struct UnkInput3Header {
- u64 timestamp_ticks;
- u64 num_entries;
- u64 latest_entry;
- u64 max_entry_index;
-};
-static_assert(sizeof(UnkInput3Header) == 0x20, "HID UnkInput3 header structure has incorrect size");
-
-struct UnkInput3Entry {
- u64 timestamp;
- u64 timestamp_2;
- u64 unk_8;
- u64 unk_10;
- u64 unk_18;
-};
-static_assert(sizeof(UnkInput3Entry) == 0x28, "HID UnkInput3 entry structure has incorrect size");
-
-struct UnkInput3 {
- UnkInput3Header header;
- std::array<UnkInput3Entry, 17> entries;
- std::array<u8, 0x138> padding;
-};
-static_assert(sizeof(UnkInput3) == 0x400, "HID UnkInput3 structure has incorrect size");
-
-// End UnkInput3
-
-// Begin TouchScreen
-
-struct TouchScreenHeader {
- u64 timestamp_ticks;
- u64 num_entries;
- u64 latest_entry;
- u64 max_entry_index;
- u64 timestamp;
-};
-static_assert(sizeof(TouchScreenHeader) == 0x28,
- "HID touch screen header structure has incorrect size");
-
-struct TouchScreenEntryHeader {
- u64 timestamp;
- u64 num_touches;
-};
-static_assert(sizeof(TouchScreenEntryHeader) == 0x10,
- "HID touch screen entry header structure has incorrect size");
-
-struct TouchScreenEntryTouch {
- u64 timestamp;
- u32 padding;
- u32 touch_index;
- u32 x;
- u32 y;
- u32 diameter_x;
- u32 diameter_y;
- u32 angle;
- u32 padding_2;
-};
-static_assert(sizeof(TouchScreenEntryTouch) == 0x28,
- "HID touch screen touch structure has incorrect size");
-
-struct TouchScreenEntry {
- TouchScreenEntryHeader header;
- std::array<TouchScreenEntryTouch, 16> touches;
- u64 unk;
-};
-static_assert(sizeof(TouchScreenEntry) == 0x298,
- "HID touch screen entry structure has incorrect size");
-
-struct TouchScreen {
- TouchScreenHeader header;
- std::array<TouchScreenEntry, 17> entries;
- std::array<u8, 0x3c0> padding;
-};
-static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size");
-
-// End TouchScreen
-
-// Begin Mouse
-
-struct MouseHeader {
- u64 timestamp_ticks;
- u64 num_entries;
- u64 latest_entry;
- u64 max_entry_index;
-};
-static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size");
-
-struct MouseButtonState {
- union {
- u64 hex{};
-
- // Buttons
- BitField<0, 1, u64> left;
- BitField<1, 1, u64> right;
- BitField<2, 1, u64> middle;
- BitField<3, 1, u64> forward;
- BitField<4, 1, u64> back;
- };
-};
-
-struct MouseEntry {
- u64 timestamp;
- u64 timestamp_2;
- u32 x;
- u32 y;
- u32 velocity_x;
- u32 velocity_y;
- u32 scroll_velocity_x;
- u32 scroll_velocity_y;
- MouseButtonState buttons;
-};
-static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size");
-
-struct Mouse {
- MouseHeader header;
- std::array<MouseEntry, 17> entries;
- std::array<u8, 0xB0> padding;
-};
-static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size");
-
-// End Mouse
-
-// Begin Keyboard
-
-struct KeyboardHeader {
- u64 timestamp_ticks;
- u64 num_entries;
- u64 latest_entry;
- u64 max_entry_index;
-};
-static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size");
-
-struct KeyboardModifierKeyState {
- union {
- u64 hex{};
-
- // Buttons
- BitField<0, 1, u64> lctrl;
- BitField<1, 1, u64> lshift;
- BitField<2, 1, u64> lalt;
- BitField<3, 1, u64> lmeta;
- BitField<4, 1, u64> rctrl;
- BitField<5, 1, u64> rshift;
- BitField<6, 1, u64> ralt;
- BitField<7, 1, u64> rmeta;
- BitField<8, 1, u64> capslock;
- BitField<9, 1, u64> scrolllock;
- BitField<10, 1, u64> numlock;
- };
-};
-
-struct KeyboardEntry {
- u64 timestamp;
- u64 timestamp_2;
- KeyboardModifierKeyState modifier;
- u32 keys[8];
-};
-static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size");
-
-struct Keyboard {
- KeyboardHeader header;
- std::array<KeyboardEntry, 17> entries;
- std::array<u8, 0x28> padding;
-};
-static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size");
-
-// End Keyboard
-
-// Begin UnkInput1
-
-struct UnkInput1Header {
- u64 timestamp_ticks;
- u64 num_entries;
- u64 latest_entry;
- u64 max_entry_index;
-};
-static_assert(sizeof(UnkInput1Header) == 0x20, "HID UnkInput1 header structure has incorrect size");
-
-struct UnkInput1Entry {
- u64 timestamp;
- u64 timestamp_2;
- u64 unk_8;
- u64 unk_10;
- u64 unk_18;
-};
-static_assert(sizeof(UnkInput1Entry) == 0x28, "HID UnkInput1 entry structure has incorrect size");
-
-struct UnkInput1 {
- UnkInput1Header header;
- std::array<UnkInput1Entry, 17> entries;
- std::array<u8, 0x138> padding;
-};
-static_assert(sizeof(UnkInput1) == 0x400, "HID UnkInput1 structure has incorrect size");
-
-// End UnkInput1
-
-// Begin UnkInput2
-
-struct UnkInput2Header {
- u64 timestamp_ticks;
- u64 num_entries;
- u64 latest_entry;
- u64 max_entry_index;
-};
-static_assert(sizeof(UnkInput2Header) == 0x20, "HID UnkInput2 header structure has incorrect size");
-
-struct UnkInput2 {
- UnkInput2Header header;
- std::array<u8, 0x1E0> padding;
-};
-static_assert(sizeof(UnkInput2) == 0x200, "HID UnkInput2 structure has incorrect size");
-
-// End UnkInput2
-
-// Begin Controller
-
-struct ControllerMAC {
- u64 timestamp;
- std::array<u8, 0x8> mac;
- u64 unk;
- u64 timestamp_2;
-};
-static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size");
-
-struct ControllerHeader {
- u32 type;
- u32 is_half;
- u32 single_colors_descriptor;
- u32 single_color_body;
- u32 single_color_buttons;
- u32 split_colors_descriptor;
- u32 left_color_body;
- u32 left_color_buttons;
- u32 right_color_body;
- u32 right_color_buttons;
-};
-static_assert(sizeof(ControllerHeader) == 0x28,
- "HID controller header structure has incorrect size");
-
-struct ControllerLayoutHeader {
- u64 timestamp_ticks;
- u64 num_entries;
- u64 latest_entry;
- u64 max_entry_index;
-};
-static_assert(sizeof(ControllerLayoutHeader) == 0x20,
- "HID controller layout header structure has incorrect size");
-
-struct ControllerPadState {
- union {
- u64 hex{};
-
- // Buttons
- BitField<0, 1, u64> a;
- BitField<1, 1, u64> b;
- BitField<2, 1, u64> x;
- BitField<3, 1, u64> y;
- BitField<4, 1, u64> lstick;
- BitField<5, 1, u64> rstick;
- BitField<6, 1, u64> l;
- BitField<7, 1, u64> r;
- BitField<8, 1, u64> zl;
- BitField<9, 1, u64> zr;
- BitField<10, 1, u64> plus;
- BitField<11, 1, u64> minus;
-
- // D-pad buttons
- BitField<12, 1, u64> dleft;
- BitField<13, 1, u64> dup;
- BitField<14, 1, u64> dright;
- BitField<15, 1, u64> ddown;
-
- // Left stick directions
- BitField<16, 1, u64> lstick_left;
- BitField<17, 1, u64> lstick_up;
- BitField<18, 1, u64> lstick_right;
- BitField<19, 1, u64> lstick_down;
-
- // Right stick directions
- BitField<20, 1, u64> rstick_left;
- BitField<21, 1, u64> rstick_up;
- BitField<22, 1, u64> rstick_right;
- BitField<23, 1, u64> rstick_down;
-
- BitField<24, 1, u64> sl;
- BitField<25, 1, u64> sr;
- };
-};
-
-struct ControllerInputEntry {
- u64 timestamp;
- u64 timestamp_2;
- ControllerPadState buttons;
- s32 joystick_left_x;
- s32 joystick_left_y;
- s32 joystick_right_x;
- s32 joystick_right_y;
- u64 connection_state;
-};
-static_assert(sizeof(ControllerInputEntry) == 0x30,
- "HID controller input entry structure has incorrect size");
-
-struct ControllerLayout {
- ControllerLayoutHeader header;
- std::array<ControllerInputEntry, 17> entries;
-};
-static_assert(sizeof(ControllerLayout) == 0x350,
- "HID controller layout structure has incorrect size");
-
-struct Controller {
- ControllerHeader header;
- std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts;
- std::array<u8, 0x2a70> unk_1;
- ControllerMAC mac_left;
- ControllerMAC mac_right;
- std::array<u8, 0xdf8> unk_2;
-};
-static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size");
-
-// End Controller
-
-struct SharedMemory {
- UnkInput3 unk_input_3;
- TouchScreen touchscreen;
- Mouse mouse;
- Keyboard keyboard;
- std::array<UnkInput1, 4> unk_input_1;
- std::array<UnkInput2, 3> unk_input_2;
- std::array<u8, 0x800> unk_section_8;
- std::array<u8, 0x4000> controller_serials;
- std::array<Controller, 10> controllers;
- std::array<u8, 0x4600> unk_section_9;
-};
-static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size");
-
/// Reload input devices. Used when input configuration changed
void ReloadInputDevices();
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index 7b91bb258..e1f17a926 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -14,14 +14,14 @@ public:
explicit MM_U() : ServiceFramework{"mm:u"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &MM_U::Initialize, "InitializeOld"},
- {1, &MM_U::Finalize, "FinalizeOld"},
- {2, &MM_U::SetAndWait, "SetAndWaitOld"},
- {3, &MM_U::Get, "GetOld"},
- {4, &MM_U::Initialize, "Initialize"},
- {5, &MM_U::Finalize, "Finalize"},
- {6, &MM_U::SetAndWait, "SetAndWait"},
- {7, &MM_U::Get, "Get"},
+ {0, &MM_U::Initialize, "Initialize"},
+ {1, &MM_U::Finalize, "Finalize"},
+ {2, &MM_U::SetAndWait, "SetAndWait"},
+ {3, &MM_U::Get, "Get"},
+ {4, &MM_U::InitializeWithId, "InitializeWithId"},
+ {5, &MM_U::FinalizeWithId, "FinalizeWithId"},
+ {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"},
+ {7, &MM_U::GetWithId, "GetWithId"},
};
// clang-format on
@@ -59,9 +59,43 @@ private:
rb.Push(current);
}
+ void InitializeWithId(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_MM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(id); // Any non zero value
+ }
+
+ void FinalizeWithId(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_MM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetAndWaitWithId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ u32 input_id = rp.Pop<u32>();
+ min = rp.Pop<u32>();
+ max = rp.Pop<u32>();
+ current = min;
+
+ LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
+ input_id, min, max);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetWithId(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_MM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(current);
+ }
+
u32 min{0};
u32 max{0};
u32 current{0};
+ u32 id{1};
};
void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 8c07a05c2..39c0c1e63 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -144,7 +144,7 @@ private:
}
const u64 device_handle{0xDEAD};
- const HID::ControllerID npad_id{HID::Controller_Player1};
+ const u32 npad_id{0}; // This is the first player controller id
State state{State::NonInitialized};
DeviceState device_state{DeviceState::Initialized};
Kernel::SharedPtr<Kernel::Event> activate_event;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 10611ed6a..75dcd94a3 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -219,6 +219,7 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
{35, nullptr, "GetScanData"},
{36, nullptr, "GetCurrentAccessPoint"},
{37, nullptr, "Shutdown"},
+ {38, nullptr, "GetAllowedChannels"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 261ad539c..18091c9bb 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -71,6 +71,22 @@ public:
}
};
+class NIM_ECA final : public ServiceFramework<NIM_ECA> {
+public:
+ explicit NIM_ECA() : ServiceFramework{"nim:eca"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CreateServerInterface"},
+ {1, nullptr, "RefreshDebugAvailability"},
+ {2, nullptr, "ClearDebugResponse"},
+ {3, nullptr, "RegisterDebugResponse"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
class NIM_SHP final : public ServiceFramework<NIM_SHP> {
public:
explicit NIM_SHP() : ServiceFramework{"nim:shp"} {
@@ -214,6 +230,7 @@ private:
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<NIM>()->InstallAsService(sm);
+ std::make_shared<NIM_ECA>()->InstallAsService(sm);
std::make_shared<NIM_SHP>()->InstallAsService(sm);
std::make_shared<NTC>()->InstallAsService(sm);
}
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 98017267c..07c1381fe 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -93,13 +93,23 @@ public:
{86, nullptr, "EnableApplicationCrashReport"},
{87, nullptr, "IsApplicationCrashReportEnabled"},
{90, nullptr, "BoostSystemMemoryResourceLimit"},
+ {91, nullptr, "Unknown1"},
+ {92, nullptr, "Unknown2"},
+ {93, nullptr, "GetMainApplicationProgramIndex"},
+ {94, nullptr, "LaunchApplication2"},
+ {95, nullptr, "GetApplicationLaunchInfo"},
+ {96, nullptr, "AcquireApplicationLaunchInfo"},
+ {97, nullptr, "GetMainApplicationProgramIndex2"},
+ {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{100, nullptr, "ResetToFactorySettings"},
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
{200, nullptr, "CalculateUserSaveDataStatistics"},
{201, nullptr, "DeleteUserSaveDataAll"},
{210, nullptr, "DeleteUserSystemSaveData"},
+ {211, nullptr, "DeleteSaveData"},
{220, nullptr, "UnregisterNetworkServiceAccount"},
+ {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
{300, nullptr, "GetApplicationShellEvent"},
{301, nullptr, "PopApplicationShellEventInfo"},
{302, nullptr, "LaunchLibraryApplet"},
@@ -114,6 +124,7 @@ public:
{403, nullptr, "GetMaxApplicationControlCacheCount"},
{404, nullptr, "InvalidateApplicationControlCache"},
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
+ {406, nullptr, "GetApplicationControlProperty"},
{502, nullptr, "RequestCheckGameCardRegistration"},
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
{504, nullptr, "RequestRegisterGameCard"},
@@ -129,6 +140,7 @@ public:
{604, nullptr, "RegisterContentsExternalKey"},
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
{606, nullptr, "GetContentMetaStorage"},
+ {607, nullptr, "ListAvailableAddOnContent"},
{700, nullptr, "PushDownloadTaskList"},
{701, nullptr, "ClearTaskStatusList"},
{702, nullptr, "RequestDownloadTaskList"},
@@ -148,6 +160,9 @@ public:
{907, nullptr, "WithdrawApplicationUpdateRequest"},
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
+ {910, nullptr, "Unknown3"},
+ {911, nullptr, "SetPreInstalledApplication"},
+ {912, nullptr, "ClearPreInstalledApplicationFlag"},
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
{1001, nullptr, "CorruptApplicationForDebug"},
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
@@ -162,6 +177,8 @@ public:
{1305, nullptr, "TryDeleteRunningApplicationEntity"},
{1306, nullptr, "TryDeleteRunningApplicationCompletely"},
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
+ {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
+ {1309, nullptr, "CleanupUnavailableAddOnContents"},
{1400, nullptr, "PrepareShutdown"},
{1500, nullptr, "FormatSdCard"},
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
@@ -199,6 +216,28 @@ public:
{2015, nullptr, "CompareSystemDeliveryInfo"},
{2016, nullptr, "ListNotCommittedContentMeta"},
{2017, nullptr, "CreateDownloadTask"},
+ {2018, nullptr, "Unknown4"},
+ {2050, nullptr, "Unknown5"},
+ {2100, nullptr, "Unknown6"},
+ {2101, nullptr, "Unknown7"},
+ {2150, nullptr, "CreateRightsEnvironment"},
+ {2151, nullptr, "DestroyRightsEnvironment"},
+ {2152, nullptr, "ActivateRightsEnvironment"},
+ {2153, nullptr, "DeactivateRightsEnvironment"},
+ {2154, nullptr, "ForceActivateRightsContextForExit"},
+ {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
+ {2161, nullptr, "SetUsersToRightsEnvironment"},
+ {2170, nullptr, "GetRightsEnvironmentStatus"},
+ {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
+ {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
+ {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
+ {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
+ {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
+ {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
+ {2200, nullptr, "Unknown8"},
+ {2201, nullptr, "Unknown9"},
+ {2250, nullptr, "Unknown10"},
+ {2300, nullptr, "Unknown11"},
};
// clang-format on
@@ -348,12 +387,15 @@ public:
{0, nullptr, "LaunchProgram"},
{1, nullptr, "TerminateProcess"},
{2, nullptr, "TerminateProgram"},
- {3, nullptr, "GetShellEventHandle"},
- {4, nullptr, "GetShellEventInfo"},
- {5, nullptr, "TerminateApplication"},
- {6, nullptr, "PrepareLaunchProgramFromHost"},
- {7, nullptr, "LaunchApplication"},
- {8, nullptr, "LaunchApplicationWithStorageId"},
+ {4, nullptr, "GetShellEventHandle"},
+ {5, nullptr, "GetShellEventInfo"},
+ {6, nullptr, "TerminateApplication"},
+ {7, nullptr, "PrepareLaunchProgramFromHost"},
+ {8, nullptr, "LaunchApplication"},
+ {9, nullptr, "LaunchApplicationWithStorageId"},
+ {10, nullptr, "TerminateApplication2"},
+ {11, nullptr, "GetRunningApplicationProcessId"},
+ {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
};
// clang-format on
@@ -388,6 +430,7 @@ public:
{19, nullptr, "GetReceivedEulaDataSize"},
{20, nullptr, "GetReceivedEulaData"},
{21, nullptr, "SetupToReceiveSystemUpdate"},
+ {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
};
// clang-format on
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/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 5af356d10..34654bb07 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -39,7 +39,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
{29, nullptr, "GetAmiiboEcqvBlsKey"},
{30, nullptr, "GetAmiiboEcqvBlsCertificate"},
{31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
- {32, nullptr, "GetUnknownId"},
+ {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
+ {33, nullptr, "GetBatteryVersion"},
};
RegisterHandlers(functions);
}
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/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 576fe692a..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,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')) {
@@ -138,7 +138,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
}
// Build program image
- 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 {};
}
@@ -182,7 +182,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
- GDBStub::RegisterModule(file->GetName(), load_base, load_base);
+ GDBStub::RegisterModule(file.GetName(), load_base, load_base);
return true;
}
@@ -195,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 ba57db9bf..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -93,17 +93,14 @@ 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'))
@@ -114,7 +111,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
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]);
}
@@ -157,7 +154,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
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());
@@ -172,7 +169,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
- GDBStub::RegisterModule(file->GetName(), load_base, load_base);
+ GDBStub::RegisterModule(file.GetName(), load_base, load_base);
return load_base + image_size;
}
@@ -184,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/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 7a619acb4..461607c95 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -59,8 +59,7 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
if (xci->GetProgramNCAStatus() != ResultStatus::Success)
return xci->GetProgramNCAStatus();
- const auto nca = xci->GetProgramNCA();
- if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
+ if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
return ResultStatus::ErrorMissingProductionKeyFile;
const auto result = nca_loader->Load(process);
diff --git a/src/core/settings.h b/src/core/settings.h
index 83b9a04c8..8f2da01c8 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -136,7 +136,7 @@ struct Values {
float resolution_factor;
bool use_frame_limit;
u16 frame_limit;
- bool use_accurate_framebuffers;
+ bool use_accurate_gpu_emulation;
float bg_red;
float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 7b04792b5..0de13edd3 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -163,8 +163,8 @@ TelemetrySession::TelemetrySession() {
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
Settings::values.use_frame_limit);
AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
- AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers",
- Settings::values.use_accurate_framebuffers);
+ AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
+ Settings::values.use_accurate_gpu_emulation);
AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
Settings::values.use_docked_mode);
}