summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/ipc_helpers.h3
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/errors.h23
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp25
-rw-r--r--src/core/hle/kernel/hle_ipc.h21
-rw-r--r--src/core/hle/kernel/kernel.cpp26
-rw-r--r--src/core/hle/kernel/kernel.h16
-rw-r--r--src/core/hle/kernel/mutex.cpp2
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp40
-rw-r--r--src/core/hle/kernel/process.h76
-rw-r--r--src/core/hle/kernel/scheduler.cpp32
-rw-r--r--src/core/hle/kernel/scheduler.h19
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_session.cpp7
-rw-r--r--src/core/hle/kernel/shared_memory.cpp7
-rw-r--r--src/core/hle/kernel/svc.cpp448
-rw-r--r--src/core/hle/kernel/svc.h31
-rw-r--r--src/core/hle/kernel/svc_wrap.h64
-rw-r--r--src/core/hle/kernel/thread.cpp39
-rw-r--r--src/core/hle/kernel/thread.h19
-rw-r--r--src/core/hle/kernel/vm_manager.cpp75
-rw-r--r--src/core/hle/kernel/vm_manager.h23
-rw-r--r--src/core/hle/service/acc/acc.cpp76
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp204
-rw-r--r--src/core/hle/service/acc/profile_manager.h38
-rw-r--r--src/core/hle/service/am/am.cpp125
-rw-r--r--src/core/hle/service/am/am.h4
-rw-r--r--src/core/hle/service/am/applet_ae.cpp4
-rw-r--r--src/core/hle/service/am/applet_oe.cpp3
-rw-r--r--src/core/hle/service/am/idle.cpp6
-rw-r--r--src/core/hle/service/am/omm.cpp34
-rw-r--r--src/core/hle/service/am/tcap.cpp23
-rw-r--r--src/core/hle/service/am/tcap.h17
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp47
-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/audio/hwopus.cpp39
-rw-r--r--src/core/hle/service/es/es.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp38
-rw-r--r--src/core/hle/service/filesystem/filesystem.h13
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp34
-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.cpp620
-rw-r--r--src/core/hle/service/hid/controllers/npad.h289
-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.cpp518
-rw-r--r--src/core/hle/service/hid/hid.h402
-rw-r--r--src/core/hle/service/lbl/lbl.cpp56
-rw-r--r--src/core/hle/service/ldr/ldr.cpp52
-rw-r--r--src/core/hle/service/mm/mm_u.cpp50
-rw-r--r--src/core/hle/service/nfc/nfc.cpp53
-rw-r--r--src/core/hle/service/nfp/nfp.cpp270
-rw-r--r--src/core/hle/service/nfp/nfp.h23
-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/npns/npns.cpp88
-rw-r--r--src/core/hle/service/npns/npns.h15
-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/nvdrv/devices/nvhost_as_gpu.cpp27
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp72
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp12
-rw-r--r--src/core/hle/service/prepo/prepo.cpp21
-rw-r--r--src/core/hle/service/ptm/psm.cpp71
-rw-r--r--src/core/hle/service/ptm/psm.h15
-rw-r--r--src/core/hle/service/service.cpp12
-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/usb/usb.cpp49
-rw-r--r--src/core/hle/service/vi/vi.cpp59
89 files changed, 4086 insertions, 1330 deletions
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 419f45896..ed84197b3 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -14,11 +14,6 @@ namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
-// These errors are commonly returned by invalid IPC translations, so alias them here for
-// convenience.
-// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
-constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
-
enum class ControlCommand : u32 {
ConvertSessionToDomain = 0,
ConvertDomainToSession = 1,
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index a4bfe2eb0..0a7142ada 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -117,8 +117,7 @@ public:
AlignWithPadding();
- const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
- if (context.Session()->IsDomain() && request_has_domain_header) {
+ if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
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/errors.h b/src/core/hle/kernel/errors.h
index e5fa67ae8..ee698c8a7 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -10,11 +10,6 @@ namespace Kernel {
namespace ErrCodes {
enum {
- // TODO(Subv): Remove these 3DS OS error codes.
- SessionClosedByRemote = 26,
- NoPendingSessions = 35,
- InvalidBufferDescriptor = 48,
-
// Confirmed Switch OS error codes
MaxConnectionsReached = 7,
InvalidSize = 101,
@@ -22,9 +17,11 @@ enum {
HandleTableFull = 105,
InvalidMemoryState = 106,
InvalidMemoryPermissions = 108,
+ InvalidMemoryRange = 110,
InvalidThreadPriority = 112,
InvalidProcessorId = 113,
InvalidHandle = 114,
+ InvalidPointer = 115,
InvalidCombination = 116,
Timeout = 117,
SynchronizationCanceled = 118,
@@ -32,6 +29,7 @@ enum {
InvalidEnumValue = 120,
NoSuchEntry = 121,
AlreadyRegistered = 122,
+ SessionClosed = 123,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
@@ -40,22 +38,19 @@ enum {
// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
// double check that the code matches before re-using the constant.
-// TODO(bunnei): Replace -1 with correct errors for Switch OS
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
-constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
+constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
ErrCodes::MaxConnectionsReached);
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
-constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
-constexpr ResultCode ERR_INVALID_COMBINATION(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
ErrCodes::InvalidCombination);
-constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
ErrCodes::InvalidMemoryPermissions);
+constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
@@ -63,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
ErrCodes::InvalidThreadPriority);
-constexpr ResultCode ERR_INVALID_POINTER(-1);
-constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
-constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
-/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
-constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
+constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
-/// Returned when Accept() is called on a port with no sessions to be accepted.
-constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index edad5f1b1..68d5376cb 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses
HLERequestContext::~HLERequestContext() = default;
-void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
+void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
+ bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
@@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
rp.Skip(2, false);
}
if (incoming) {
- auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
-
// Populate the object lists with the data in the IPC request.
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
@@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
}
-ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf,
- Process& src_process,
- HandleTable& src_table) {
- ParseCommandBuffer(src_cmdbuf, true);
+ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
+ u32_le* src_cmdbuf) {
+ ParseCommandBuffer(handle_table, src_cmdbuf, true);
if (command_header->type == IPC::CommandType::Close) {
// Close does not populate the rest of the IPC header
return RESULT_SUCCESS;
@@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
-ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
+ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
+ auto& owner_process = *thread.GetOwnerProcess();
+ auto& handle_table = owner_process.GetHandleTable();
+
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
- Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
+ Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
// The header was already built in the internal command buffer. Attempt to parse it to verify
// the integrity and then copy it over to the target command buffer.
- ParseCommandBuffer(cmd_buf.data(), false);
+ ParseCommandBuffer(handle_table, cmd_buf.data(), false);
// The data_size already includes the payload header, the padding and the domain header.
std::size_t size = data_payload_offset + command_header->data_size -
@@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
- auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
-
// We don't make a distinction between copy and move handles when translating since HLE
// services don't deal with handles directly. However, the guest applications might check
// for specific values in each of these descriptors.
@@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
}
// Copy the translated command buffer back into the thread's command buffer area.
- Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
+ Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
return RESULT_SUCCESS;
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 894479ee0..a38e34b74 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -24,10 +24,10 @@ class ServiceFrameworkBase;
namespace Kernel {
class Domain;
+class Event;
class HandleTable;
class HLERequestContext;
class Process;
-class Event;
/**
* Interface implemented by HLE Session handlers.
@@ -126,13 +126,12 @@ public:
u64 timeout, WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event = nullptr);
- void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
-
/// Populates this context with data from the requesting process/thread.
- ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
- HandleTable& src_table);
+ ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
+ u32_le* src_cmdbuf);
+
/// Writes data from this context back to the requesting process/thread.
- ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
+ ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
u32_le GetCommand() const {
return command;
@@ -162,8 +161,12 @@ public:
return buffer_c_desciptors;
}
- const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
- return domain_message_header;
+ const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
+ return domain_message_header.get();
+ }
+
+ bool HasDomainMessageHeader() const {
+ return domain_message_header != nullptr;
}
/// Helper function to read a buffer using the appropriate buffer descriptor
@@ -255,6 +258,8 @@ public:
std::string Description() const;
private:
+ void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
+
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;
// TODO(yuriks): Check common usage of this and optimize size accordingly
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 98eb74298..1fd4ba5d2 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -32,7 +32,7 @@ namespace Kernel {
*/
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
- auto& system = Core::System::GetInstance();
+ const auto& system = Core::System::GetInstance();
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
@@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(timer_handle);
- auto& system = Core::System::GetInstance();
+ const auto& system = Core::System::GetInstance();
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
if (timer == nullptr) {
@@ -116,9 +116,8 @@ struct KernelCore::Impl {
next_thread_id = 1;
process_list.clear();
- current_process.reset();
+ current_process = nullptr;
- handle_table.Clear();
resource_limits.fill(nullptr);
thread_wakeup_callback_handle_table.Clear();
@@ -207,9 +206,8 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::vector<SharedPtr<Process>> process_list;
- SharedPtr<Process> current_process;
+ Process* current_process = nullptr;
- Kernel::HandleTable handle_table;
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
/// The event type of the generic timer callback event
@@ -241,14 +239,6 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
-Kernel::HandleTable& KernelCore::HandleTable() {
- return impl->handle_table;
-}
-
-const Kernel::HandleTable& KernelCore::HandleTable() const {
- return impl->handle_table;
-}
-
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
ResourceLimitCategory category) const {
return impl->resource_limits.at(static_cast<std::size_t>(category));
@@ -266,15 +256,15 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
impl->process_list.push_back(std::move(process));
}
-void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) {
- impl->current_process = std::move(process);
+void KernelCore::MakeCurrentProcess(Process* process) {
+ impl->current_process = process;
}
-SharedPtr<Process>& KernelCore::CurrentProcess() {
+Process* KernelCore::CurrentProcess() {
return impl->current_process;
}
-const SharedPtr<Process>& KernelCore::CurrentProcess() const {
+const Process* KernelCore::CurrentProcess() const {
return impl->current_process;
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c0771ecf0..7f822d524 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -47,12 +47,6 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
- /// Provides a reference to the handle table.
- Kernel::HandleTable& HandleTable();
-
- /// Provides a const reference to the handle table.
- const Kernel::HandleTable& HandleTable() const;
-
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
@@ -66,13 +60,13 @@ public:
void AppendNewProcess(SharedPtr<Process> process);
/// Makes the given process the new current process.
- void MakeCurrentProcess(SharedPtr<Process> process);
+ void MakeCurrentProcess(Process* process);
- /// Retrieves a reference to the current process.
- SharedPtr<Process>& CurrentProcess();
+ /// Retrieves a pointer to the current process.
+ Process* CurrentProcess();
- /// Retrieves a const reference to the current process.
- const SharedPtr<Process>& CurrentProcess() const;
+ /// Retrieves a const pointer to the current process.
+ const Process* CurrentProcess() const;
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index dd541ffcc..0743670ad 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -6,8 +6,6 @@
#include <utility>
#include <vector>
-#include <boost/range/algorithm_ext/erase.hpp>
-
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index d51562d92..d87a62bb9 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
- case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 9eb72315c..c9f4d0bb3 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -26,7 +26,6 @@ enum class HandleType : u32 {
AddressArbiter,
Timer,
ResourceLimit,
- CodeSet,
ClientPort,
ServerPort,
ClientSession,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index fb0027a71..420218d59 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,13 +20,7 @@
namespace Kernel {
-SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) {
- SharedPtr<CodeSet> codeset(new CodeSet(kernel));
- codeset->name = std::move(name);
- return codeset;
-}
-
-CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
+CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -159,11 +153,11 @@ void Process::PrepareForTermination() {
}
};
- auto& system = Core::System::GetInstance();
- stop_threads(system.Scheduler(0)->GetThreadList());
- stop_threads(system.Scheduler(1)->GetThreadList());
- stop_threads(system.Scheduler(2)->GetThreadList());
- stop_threads(system.Scheduler(3)->GetThreadList());
+ const auto& system = Core::System::GetInstance();
+ stop_threads(system.Scheduler(0).GetThreadList());
+ stop_threads(system.Scheduler(1).GetThreadList());
+ stop_threads(system.Scheduler(2).GetThreadList());
+ stop_threads(system.Scheduler(3).GetThreadList());
}
/**
@@ -224,20 +218,26 @@ void Process::FreeTLSSlot(VAddr tls_address) {
tls_slots[tls_page].reset(tls_slot);
}
-void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
+void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
- auto vma = vm_manager
- .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
- segment.size, memory_state)
- .Unwrap();
+ const auto vma = vm_manager
+ .MapMemoryBlock(segment.addr + base_addr, module_.memory,
+ segment.offset, segment.size, memory_state)
+ .Unwrap();
vm_manager.Reprotect(vma, permissions);
};
// Map CodeSet segments
- MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
- MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
- MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
+ MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
+ MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
+ MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
+
+ // Clear instruction cache in CPU JIT
+ Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 590e0c73d..8d2616c79 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -13,6 +13,7 @@
#include <boost/container/static_vector.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
+#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
@@ -24,6 +25,7 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
+class ResourceLimit;
struct AddressMapping {
// Address and size must be page-aligned
@@ -57,30 +59,33 @@ union ProcessFlags {
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
};
-enum class ProcessStatus { Created, Running, Exited };
-
-class ResourceLimit;
+/**
+ * Indicates the status of a Process instance.
+ *
+ * @note These match the values as used by kernel,
+ * so new entries should only be added if RE
+ * shows that a new value has been introduced.
+ */
+enum class ProcessStatus {
+ Created,
+ CreatedWithDebuggerAttached,
+ Running,
+ WaitingForDebuggerToAttach,
+ DebuggerAttached,
+ Exiting,
+ Exited,
+ DebugBreak,
+};
-struct CodeSet final : public Object {
+struct CodeSet final {
struct Segment {
std::size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
- static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name);
-
- std::string GetTypeName() const override {
- return "CodeSet";
- }
- std::string GetName() const override {
- return name;
- }
-
- static const HandleType HANDLE_TYPE = HandleType::CodeSet;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
+ explicit CodeSet();
+ ~CodeSet();
Segment& CodeSegment() {
return segments[0];
@@ -109,14 +114,7 @@ struct CodeSet final : public Object {
std::shared_ptr<std::vector<u8>> memory;
std::array<Segment, 3> segments;
- VAddr entrypoint;
-
- /// Name of the process
- std::string name;
-
-private:
- explicit CodeSet(KernelCore& kernel);
- ~CodeSet() override;
+ VAddr entrypoint = 0;
};
class Process final : public Object {
@@ -145,6 +143,16 @@ public:
return vm_manager;
}
+ /// Gets a reference to the process' handle table.
+ HandleTable& GetHandleTable() {
+ return handle_table;
+ }
+
+ /// Gets a const reference to the process' handle table.
+ const HandleTable& GetHandleTable() const {
+ return handle_table;
+ }
+
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -194,6 +202,16 @@ public:
return is_64bit_process;
}
+ /// Gets the total running time of the process instance in ticks.
+ u64 GetCPUTimeTicks() const {
+ return total_process_running_time_ticks;
+ }
+
+ /// Updates the total running time, adding the given ticks to it.
+ void UpdateCPUTimeTicks(u64 ticks) {
+ total_process_running_time_ticks += ticks;
+ }
+
/**
* Loads process-specifics configuration info with metadata provided
* by an executable.
@@ -219,7 +237,7 @@ public:
*/
void PrepareForTermination();
- void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
+ void LoadModule(CodeSet module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
@@ -297,6 +315,12 @@ private:
/// specified by metadata provided to the process during loading.
bool is_64bit_process = true;
+ /// Total running time for the process in ticks.
+ u64 total_process_running_time_ticks = 0;
+
+ /// Per-process handle table for storing created object handles in.
+ HandleTable handle_table;
+
std::string name;
};
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index cfd6e1bad..5a5f4cef1 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -10,6 +10,7 @@
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
@@ -34,6 +35,10 @@ Thread* Scheduler::GetCurrentThread() const {
return current_thread.get();
}
+u64 Scheduler::GetLastContextSwitchTicks() const {
+ return last_context_switch_time;
+}
+
Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
@@ -54,7 +59,10 @@ Thread* Scheduler::PopNextReadyThread() {
}
void Scheduler::SwitchContext(Thread* new_thread) {
- Thread* previous_thread = GetCurrentThread();
+ Thread* const previous_thread = GetCurrentThread();
+ Process* const previous_process = Core::CurrentProcess();
+
+ UpdateLastContextSwitchTime(previous_thread, previous_process);
// Save context for previous thread
if (previous_thread) {
@@ -78,16 +86,14 @@ void Scheduler::SwitchContext(Thread* new_thread) {
// Cancel any outstanding wakeup events for this thread
new_thread->CancelWakeupTimer();
- auto previous_process = Core::CurrentProcess();
-
current_thread = new_thread;
ready_queue.remove(new_thread->GetPriority(), new_thread);
new_thread->SetStatus(ThreadStatus::Running);
- const auto thread_owner_process = current_thread->GetOwnerProcess();
+ auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
- Core::CurrentProcess() = thread_owner_process;
+ Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
}
@@ -102,6 +108,22 @@ void Scheduler::SwitchContext(Thread* new_thread) {
}
}
+void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
+ const u64 prev_switch_ticks = last_context_switch_time;
+ const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
+ const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
+
+ if (thread != nullptr) {
+ thread->UpdateCPUTimeTicks(update_ticks);
+ }
+
+ if (process != nullptr) {
+ process->UpdateCPUTimeTicks(update_ticks);
+ }
+
+ last_context_switch_time = most_recent_switch_ticks;
+}
+
void Scheduler::Reschedule() {
std::lock_guard<std::mutex> lock(scheduler_mutex);
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 2c94641ec..c63032b7d 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -17,6 +17,8 @@ class ARM_Interface;
namespace Kernel {
+class Process;
+
class Scheduler final {
public:
explicit Scheduler(Core::ARM_Interface& cpu_core);
@@ -31,6 +33,9 @@ public:
/// Gets the current running thread
Thread* GetCurrentThread() const;
+ /// Gets the timestamp for the last context switch in ticks.
+ u64 GetLastContextSwitchTicks() const;
+
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread, u32 priority);
@@ -64,6 +69,19 @@ private:
*/
void SwitchContext(Thread* new_thread);
+ /**
+ * Called on every context switch to update the internal timestamp
+ * This also updates the running time ticks for the given thread and
+ * process using the following difference:
+ *
+ * ticks += most_recent_ticks - last_context_switch_ticks
+ *
+ * The internal tick timestamp for the scheduler is simply the
+ * most recent tick count retrieved. No special arithmetic is
+ * applied to it.
+ */
+ void UpdateLastContextSwitchTime(Thread* thread, Process* process);
+
/// Lists all thread ids that aren't deleted/etc.
std::vector<SharedPtr<Thread>> thread_list;
@@ -73,6 +91,7 @@ private:
SharedPtr<Thread> current_thread = nullptr;
Core::ARM_Interface& cpu_core;
+ u64 last_context_switch_time = 0;
static std::mutex scheduler_mutex;
};
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 3792e3e18..d6ceeb2da 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
if (pending_sessions.empty()) {
- return ERR_NO_PENDING_SESSIONS;
+ return ERR_NOT_FOUND;
}
auto session = std::move(pending_sessions.back());
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
bool ServerPort::ShouldWait(Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
- return pending_sessions.size() == 0;
+ return pending_sessions.empty();
}
void ServerPort::Acquire(Thread* thread) {
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 1ece691c7..80897f3a4 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) {
}
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
- auto& domain_message_header = context.GetDomainMessageHeader();
+ auto* const domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
context.SetDomainRequestHandlers(domain_request_handlers);
@@ -107,12 +107,11 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// similar.
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
- context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
- kernel.HandleTable());
+ context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
- if (IsDomain() && context.GetDomainMessageHeader()) {
+ if (IsDomain() && context.HasDomainMessageHeader()) {
result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used
} else if (hle_handler != nullptr) {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index d061e6155..a016a86b6 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
MemoryPermission other_permissions) {
-
- MemoryPermission own_other_permissions =
+ const MemoryPermission own_other_permissions =
target_process == owner_process ? this->permissions : this->other_permissions;
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
- return ERR_INVALID_COMBINATION;
+ return ERR_INVALID_MEMORY_PERMISSIONS;
}
// Error out if the requested permissions don't match what the creator process allows.
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
GetObjectId(), address, name);
- return ERR_INVALID_COMBINATION;
+ return ERR_INVALID_MEMORY_PERMISSIONS;
}
// Error out if the provided permissions are not compatible with what the creator process needs.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 6c4af7e47..c7c579aaf 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,8 +37,72 @@
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
+// or if the given size is zero.
+constexpr bool IsValidAddressRange(VAddr address, u64 size) {
+ return address + size > address;
+}
+
+// Checks if a given address range lies within a larger address range.
+constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
+ VAddr address_range_end) {
+ const VAddr end_address = address + size - 1;
+ return address_range_begin <= address && end_address <= address_range_end - 1;
+}
+
+bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
+ return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
+ vm.GetAddressSpaceEndAddress());
+}
+
+bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
+ return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
+ vm.GetNewMapRegionEndAddress());
+}
+
+// Helper function that performs the common sanity checks for svcMapMemory
+// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
+// in the same order.
+ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
+ u64 size) {
+ if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(dst_addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!IsValidAddressRange(src_addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ const VAddr dst_end_address = dst_addr + size;
+ if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
+ vm_manager.GetHeapRegionEndAddress() > dst_addr) {
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
+ vm_manager.GetMapRegionEndAddress() > dst_addr) {
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return RESULT_SUCCESS;
}
} // Anonymous namespace
@@ -69,15 +134,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
- return ERR_INVALID_ADDRESS;
- }
+ auto* const current_process = Core::CurrentProcess();
+ const auto& vm_manager = current_process->VMManager();
- if (size == 0 || !Is4KBAligned(size)) {
- return ERR_INVALID_SIZE;
+ const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
+ if (result != RESULT_SUCCESS) {
+ return result;
}
- return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
+ return current_process->MirrorMemory(dst_addr, src_addr, size);
}
/// Unmaps a region that was previously mapped with svcMapMemory
@@ -85,15 +150,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
- return ERR_INVALID_ADDRESS;
- }
+ auto* const current_process = Core::CurrentProcess();
+ const auto& vm_manager = current_process->VMManager();
- if (size == 0 || !Is4KBAligned(size)) {
- return ERR_INVALID_SIZE;
+ const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
+ if (result != RESULT_SUCCESS) {
+ return result;
}
- return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
+ return current_process->UnmapMemory(dst_addr, src_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -124,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
CASCADE_RESULT(client_session, client_port->Connect());
// Return the client session
- CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
+ auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
return RESULT_SUCCESS;
}
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Handle handle) {
- auto& kernel = Core::System::GetInstance().Kernel();
- SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
@@ -150,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) {
static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -164,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
@@ -208,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
using ObjectPtr = Thread::ThreadWaitObjects::value_type;
Thread::ThreadWaitObjects objects(handle_count);
- auto& kernel = Core::System::GetInstance().Kernel();
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
for (u64 i = 0; i < handle_count; ++i) {
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
- const auto object = kernel.HandleTable().Get<WaitObject>(handle);
+ const auto object = handle_table.Get<WaitObject>(handle);
if (object == nullptr) {
return ERR_INVALID_HANDLE;
@@ -260,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
static ResultCode CancelSynchronization(Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -285,7 +351,11 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
return ERR_INVALID_ADDRESS_STATE;
}
- auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
+ if (!Common::IsWordAligned(mutex_addr)) {
+ return ERR_INVALID_ADDRESS;
+ }
+
+ auto& handle_table = Core::CurrentProcess()->GetHandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
}
@@ -298,16 +368,87 @@ 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);
}
+enum class BreakType : u32 {
+ Panic = 0,
+ AssertionFailed = 1,
+ PreNROLoad = 3,
+ PostNROLoad = 4,
+ PreNROUnload = 5,
+ PostNROUnload = 6,
+};
+
+struct BreakReason {
+ union {
+ u32 raw;
+ BitField<0, 30, BreakType> break_type;
+ BitField<31, 1, u32> signal_debugger;
+ };
+};
+
/// Break program execution
-static void Break(u64 reason, u64 info1, u64 info2) {
- LOG_CRITICAL(
- Debug_Emulated,
- "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
- reason, info1, info2);
- ASSERT(false);
+static void Break(u32 reason, u64 info1, u64 info2) {
+ BreakReason break_reason{reason};
+
+ switch (break_reason.break_type) {
+ case BreakType::Panic:
+ LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
+ info1, info2);
+ break;
+ case BreakType::AssertionFailed:
+ LOG_CRITICAL(Debug_Emulated,
+ "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
+ info1, info2);
+ break;
+ case BreakType::PreNROLoad:
+ LOG_WARNING(
+ Debug_Emulated,
+ "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
+ info1, info2);
+ break;
+ case BreakType::PostNROLoad:
+ LOG_WARNING(Debug_Emulated,
+ "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
+ break;
+ case BreakType::PreNROUnload:
+ LOG_WARNING(
+ Debug_Emulated,
+ "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
+ info1, info2);
+ break;
+ case BreakType::PostNROUnload:
+ LOG_WARNING(Debug_Emulated,
+ "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
+ break;
+ default:
+ LOG_WARNING(
+ Debug_Emulated,
+ "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
+ static_cast<u32>(break_reason.break_type.Value()), info1, info2);
+ break;
+ }
+
+ if (!break_reason.signal_debugger) {
+ LOG_CRITICAL(
+ Debug_Emulated,
+ "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();
+ }
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -326,7 +467,38 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
- const auto& current_process = Core::CurrentProcess();
+ enum class GetInfoType : u64 {
+ // 1.0.0+
+ AllowedCpuIdBitmask = 0,
+ AllowedThreadPrioBitmask = 1,
+ MapRegionBaseAddr = 2,
+ MapRegionSize = 3,
+ HeapRegionBaseAddr = 4,
+ HeapRegionSize = 5,
+ TotalMemoryUsage = 6,
+ TotalHeapUsage = 7,
+ IsCurrentProcessBeingDebugged = 8,
+ ResourceHandleLimit = 9,
+ IdleTickCount = 10,
+ RandomEntropy = 11,
+ PerformanceCounter = 0xF0000002,
+ // 2.0.0+
+ ASLRRegionBaseAddr = 12,
+ ASLRRegionSize = 13,
+ NewMapRegionBaseAddr = 14,
+ NewMapRegionSize = 15,
+ // 3.0.0+
+ IsVirtualAddressMemoryEnabled = 16,
+ PersonalMmHeapUsage = 17,
+ TitleId = 18,
+ // 4.0.0+
+ PrivilegedProcessId = 19,
+ // 5.0.0+
+ UserExceptionContextAddr = 20,
+ ThreadTickCount = 0xF0000002,
+ };
+
+ const auto* current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
switch (static_cast<GetInfoType>(info_id)) {
@@ -360,25 +532,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;
@@ -401,6 +560,36 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
"(STUBBED) Attempted to query user exception context address, returned 0");
*result = 0;
break;
+ case GetInfoType::ThreadTickCount: {
+ constexpr u64 num_cpus = 4;
+ if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
+ return ERR_INVALID_COMBINATION_KERNEL;
+ }
+
+ const auto thread =
+ current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
+ if (!thread) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ const auto& system = Core::System::GetInstance();
+ const auto& scheduler = system.CurrentScheduler();
+ const auto* const current_thread = scheduler.GetCurrentThread();
+ const bool same_thread = current_thread == thread;
+
+ const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
+ u64 out_ticks = 0;
+ if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
+ const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
+
+ out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
+ } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
+ out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
+ }
+
+ *result = out_ticks;
+ break;
+ }
default:
UNIMPLEMENTED();
}
@@ -418,13 +607,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
+ const auto* current_process = Core::CurrentProcess();
+ const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
- const auto current_process = Core::CurrentProcess();
if (thread->GetOwnerProcess() != current_process) {
return ERR_INVALID_HANDLE;
}
@@ -450,10 +638,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
- if (!thread)
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
+ if (!thread) {
return ERR_INVALID_HANDLE;
+ }
*priority = thread->GetPriority();
return RESULT_SUCCESS;
@@ -465,16 +654,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
return ERR_INVALID_THREAD_PRIORITY;
}
- auto& kernel = Core::System::GetInstance().Kernel();
- SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
- if (!thread)
- return ERR_INVALID_HANDLE;
+ const auto* const current_process = Core::CurrentProcess();
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
- const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
+ const ResourceLimit& resource_limit = current_process->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_NOT_AUTHORIZED;
+ return ERR_INVALID_THREAD_PRIORITY;
+ }
+
+ SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+ if (!thread) {
+ return ERR_INVALID_HANDLE;
}
thread->SetPriority(priority);
@@ -495,14 +686,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) {
@@ -510,46 +705,61 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return ERR_INVALID_MEMORY_PERMISSIONS;
}
- auto& kernel = Core::System::GetInstance().Kernel();
- auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
+ auto* const current_process = Core::CurrentProcess();
+ auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
return ERR_INVALID_HANDLE;
}
- return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
- MemoryPermission::DontCare);
+ 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;
}
- auto& kernel = Core::System::GetInstance().Kernel();
- auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
+ if (!IsValidAddressRange(addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
- return shared_memory->Unmap(Core::CurrentProcess().get(), addr);
+ auto* const current_process = Core::CurrentProcess();
+ auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
+ if (!shared_memory) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ const auto& vm_manager = current_process->VMManager();
+ if (!vm_manager.IsWithinASLRRegion(addr, size)) {
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return shared_memory->Unmap(current_process, addr);
}
/// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
Handle process_handle, u64 addr) {
-
- auto& kernel = Core::System::GetInstance().Kernel();
- SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
auto vma = process->VMManager().FindVMA(addr);
memory_info->attributes = 0;
- if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) {
+ if (vma == process->VMManager().vma_map.end()) {
memory_info->base_address = 0;
memory_info->permission = static_cast<u32>(VMAPermission::None);
memory_info->size = 0;
@@ -573,7 +783,7 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
/// Exits the current process
static void ExitProcess() {
- auto& current_process = Core::CurrentProcess();
+ auto* current_process = Core::CurrentProcess();
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -590,20 +800,19 @@ static void ExitProcess() {
/// Creates a new thread
static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
u32 priority, s32 processor_id) {
- std::string name = fmt::format("thread-{:X}", entry_point);
-
if (priority > THREADPRIO_LOWEST) {
return ERR_INVALID_THREAD_PRIORITY;
}
- const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
+ auto* const current_process = Core::CurrentProcess();
+ const ResourceLimit& resource_limit = current_process->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_NOT_AUTHORIZED;
+ return ERR_INVALID_THREAD_PRIORITY;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
- processor_id = Core::CurrentProcess()->GetDefaultProcessorID();
+ processor_id = current_process->GetDefaultProcessorID();
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
}
@@ -618,11 +827,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
return ERR_INVALID_PROCESSOR_ID;
}
+ const std::string name = fmt::format("thread-{:X}", entry_point);
auto& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(SharedPtr<Thread> thread,
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
- Core::CurrentProcess()));
- const auto new_guest_handle = kernel.HandleTable().Create(thread);
+ *current_process));
+
+ const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
if (new_guest_handle.Failed()) {
return new_guest_handle.Code();
}
@@ -643,8 +854,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
static ResultCode StartThread(Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -691,8 +902,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
- auto& kernel = Core::System::GetInstance().Kernel();
- SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -721,7 +932,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)
@@ -803,9 +1014,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
- auto& kernel = Core::System::GetInstance().Kernel();
- Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
- auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
+ const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->InvalidateWakeupCallback();
@@ -884,16 +1095,16 @@ static u64 GetSystemTick() {
static ResultCode CloseHandle(Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- return kernel.HandleTable().Close(handle);
+ auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ return handle_table.Close(handle);
}
/// Reset an event
static ResultCode ResetSignal(Handle handle) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- auto event = kernel.HandleTable().Get<Event>(handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ auto event = handle_table.Get<Event>(handle);
ASSERT(event != nullptr);
@@ -912,8 +1123,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -928,8 +1139,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
mask, core);
- auto& kernel = Core::System::GetInstance().Kernel();
- const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -990,7 +1201,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
}
auto& kernel = Core::System::GetInstance().Kernel();
- auto& handle_table = kernel.HandleTable();
+ auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto shared_mem_handle =
SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
local_perms, remote_perms);
@@ -1002,14 +1213,39 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
- auto& kernel = Core::System::GetInstance().Kernel();
- SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle);
- if (evt == nullptr)
+ const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ SharedPtr<Event> evt = handle_table.Get<Event>(handle);
+ if (evt == nullptr) {
return ERR_INVALID_HANDLE;
+ }
+
evt->Clear();
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& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto process = handle_table.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();
@@ -1145,7 +1381,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..b06aac4ec 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -24,37 +24,6 @@ struct PageInfo {
u64 flags;
};
-/// Values accepted by svcGetInfo
-enum class GetInfoType : u64 {
- // 1.0.0+
- AllowedCpuIdBitmask = 0,
- AllowedThreadPrioBitmask = 1,
- MapRegionBaseAddr = 2,
- MapRegionSize = 3,
- HeapRegionBaseAddr = 4,
- HeapRegionSize = 5,
- TotalMemoryUsage = 6,
- TotalHeapUsage = 7,
- IsCurrentProcessBeingDebugged = 8,
- ResourceHandleLimit = 9,
- IdleTickCount = 10,
- RandomEntropy = 11,
- PerformanceCounter = 0xF0000002,
- // 2.0.0+
- AddressSpaceBaseAddr = 12,
- AddressSpaceSize = 13,
- NewMapRegionBaseAddr = 14,
- NewMapRegionSize = 15,
- // 3.0.0+
- IsVirtualAddressMemoryEnabled = 16,
- PersonalMmHeapUsage = 17,
- TitleId = 18,
- // 4.0.0+
- PrivilegedProcessId = 19,
- // 5.0.0+
- UserExceptionContextAddr = 20,
-};
-
void CallSVC(u32 immediate);
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 22712e64f..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -35,18 +35,18 @@ void SvcWrap() {
template <ResultCode func(u32)>
void SvcWrap() {
- FuncReturn(func((u32)Param(0)).raw);
+ FuncReturn(func(static_cast<u32>(Param(0))).raw);
}
template <ResultCode func(u32, u32)>
void SvcWrap() {
- FuncReturn(func((u32)Param(0), (u32)Param(1)).raw);
+ FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
}
template <ResultCode func(u32*, u32)>
void SvcWrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, (u32)Param(1)).raw;
+ u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -61,7 +61,7 @@ void SvcWrap() {
template <ResultCode func(u64, s32)>
void SvcWrap() {
- FuncReturn(func(Param(0), (s32)Param(1)).raw);
+ FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
}
template <ResultCode func(u64, u32)>
@@ -77,21 +77,29 @@ 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((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw);
+ FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
}
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
- FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw);
+ FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
}
template <ResultCode func(u32, u32*, u64*)>
void SvcWrap() {
u32 param_1 = 0;
u64 param_2 = 0;
- ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2);
+ ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
Core::CurrentArmInterface().SetReg(1, param_1);
Core::CurrentArmInterface().SetReg(2, param_2);
FuncReturn(retval.raw);
@@ -100,12 +108,12 @@ void SvcWrap() {
template <ResultCode func(u64, u64, u32, u32)>
void SvcWrap() {
FuncReturn(
- func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw);
+ func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(u32, u64, u32)>
void SvcWrap() {
- FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw);
+ FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u64, u64, u64)>
@@ -115,25 +123,28 @@ void SvcWrap() {
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
- FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw);
+ FuncReturn(
+ func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
- FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw);
+ FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;
- ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3));
+ ResultCode retval =
+ func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval.raw);
}
template <ResultCode func(u64, u64, u32, s64)>
void SvcWrap() {
- FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw);
+ FuncReturn(
+ func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
}
template <ResultCode func(u64*, u64, u64, u64)>
@@ -147,9 +158,9 @@ void SvcWrap() {
template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
void SvcWrap() {
u32 param_1 = 0;
- u32 retval =
- func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF))
- .raw;
+ u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
+ static_cast<s32>(Param(5)))
+ .raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -172,7 +183,7 @@ void SvcWrap() {
template <ResultCode func(u32*, u64, u64, u32)>
void SvcWrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw;
+ u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -181,22 +192,22 @@ template <ResultCode func(Handle*, u64, u32, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval =
- func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw;
+ func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u64, u32, s32, s64)>
void SvcWrap() {
- FuncReturn(
- func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3))
- .raw);
+ FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
+ static_cast<s64>(Param(3)))
+ .raw);
}
template <ResultCode func(u64, u32, s32, s32)>
void SvcWrap() {
- FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF),
- (s32)(Param(3) & 0xFFFFFFFF))
+ FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
+ static_cast<s32>(Param(3)))
.raw);
}
@@ -226,7 +237,7 @@ void SvcWrap() {
template <void func(s64)>
void SvcWrap() {
- func((s64)Param(0));
+ func(static_cast<s64>(Param(0)));
}
template <void func(u64, u64 len)>
@@ -239,4 +250,9 @@ void SvcWrap() {
func(Param(0), Param(1), Param(2));
}
+template <void func(u32, u64, u64)>
+void SvcWrap() {
+ func(static_cast<u32>(Param(0)), Param(1), Param(2));
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 8e514cf9a..dd5cd9ced 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -4,9 +4,9 @@
#include <algorithm>
#include <cinttypes>
+#include <optional>
#include <vector>
-#include <boost/optional.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
@@ -94,10 +94,10 @@ void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
-static boost::optional<s32> GetNextProcessorId(u64 mask) {
+static std::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;
}
@@ -142,19 +142,19 @@ void Thread::ResumeFromWait() {
status = ThreadStatus::Ready;
- boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
+ std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
if (!new_processor_id) {
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();
}
@@ -183,18 +183,15 @@ void Thread::ResumeFromWait() {
*/
static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
- memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext));
-
+ context = {};
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
- context.pstate = 0;
- context.fpcr = 0;
}
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
- VAddr stack_top, SharedPtr<Process> owner_process) {
+ VAddr stack_top, Process& owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -208,7 +205,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
// TODO(yuriks): Other checks, returning 0xD9001BEA
- if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
+ if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
// TODO (bunnei): Find the correct error code to use here
return ResultCode(-1);
@@ -232,8 +229,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->wait_handle = 0;
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->owner_process = &owner_process;
+ thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
@@ -264,12 +261,12 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
- stack_top, &owner_process);
+ stack_top, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
- const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap();
+ const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
thread->SetGuestHandle(guest_handle);
thread->GetContext().cpu_registers[1] = guest_handle;
@@ -372,20 +369,20 @@ void Thread::ChangeCore(u32 core, u64 mask) {
return;
}
- boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
+ std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
if (!new_processor_id) {
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
@@ -400,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/thread.h b/src/core/hle/kernel/thread.h
index c6ffbd28c..4a6e11239 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -89,7 +89,7 @@ public:
static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name,
VAddr entry_point, u32 priority, u64 arg,
s32 processor_id, VAddr stack_top,
- SharedPtr<Process> owner_process);
+ Process& owner_process);
std::string GetName() const override {
return name;
@@ -258,15 +258,23 @@ public:
return last_running_ticks;
}
+ u64 GetTotalCPUTimeTicks() const {
+ return total_cpu_time_ticks;
+ }
+
+ void UpdateCPUTimeTicks(u64 ticks) {
+ total_cpu_time_ticks += ticks;
+ }
+
s32 GetProcessorID() const {
return processor_id;
}
- SharedPtr<Process>& GetOwnerProcess() {
+ Process* GetOwnerProcess() {
return owner_process;
}
- const SharedPtr<Process>& GetOwnerProcess() const {
+ const Process* GetOwnerProcess() const {
return owner_process;
}
@@ -378,7 +386,8 @@ private:
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
- u64 last_running_ticks = 0; ///< CPU tick when thread was last running
+ u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
+ u64 last_running_ticks = 0; ///< CPU tick when thread was last running
s32 processor_id = 0;
@@ -386,7 +395,7 @@ private:
u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
/// Process that owns this thread
- SharedPtr<Process> owner_process;
+ Process* owner_process;
/// Objects that the thread is waiting on, in the same order as they were
/// passed to WaitSynchronization1/N.
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..1a92c8f70 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
}
+ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
+ // Find the first Free VMA.
+ const VAddr base = GetASLRRegionBaseAddress();
+ const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
+ if (vma.second.type != VMAType::Free)
+ return false;
+
+ const VAddr vma_end = vma.second.base + vma.second.size;
+ return vma_end > base && vma_end >= base + size;
+ });
+
+ if (vma_handle == vma_map.end()) {
+ // TODO(Subv): Find the correct error code here.
+ return ResultCode(-1);
+ }
+
+ const VAddr target = std::max(base, vma_handle->second.base);
+ return MakeResult<VAddr>(target);
+}
+
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
Memory::MemoryHookPointer mmio_handler) {
@@ -393,30 +413,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 +515,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..2447cbb8f 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -158,6 +158,14 @@ public:
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
/**
+ * Finds the first free address that can hold a region of the desired size.
+ *
+ * @param size Size of the desired region.
+ * @return The found free address.
+ */
+ ResultVal<VAddr> FindFreeRegion(u64 size) const;
+
+ /**
* Maps a memory-mapped IO region at a given address.
*
* @param target The guest address to start the mapping at.
@@ -205,6 +213,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 +326,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/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e61748ca3..c6437a671 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -2,9 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <array>
+#include "common/common_paths.h"
#include "common/common_types.h"
+#include "common/file_util.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
@@ -16,6 +20,7 @@
#include "core/hle/service/acc/profile_manager.h"
namespace Service::Account {
+
// TODO: RE this structure
struct UserData {
INSERT_PADDING_WORDS(1);
@@ -27,6 +32,29 @@ struct UserData {
};
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
+// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
+// used as a backup should the one on disk not exist
+constexpr u32 backup_jpeg_size = 107;
+constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
+ 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
+ 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
+ 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
+ 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
+ 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
+ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
+}};
+
+static std::string GetImagePath(UUID uuid) {
+ return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
+}
+
+static constexpr u32 SanitizeJPEGSize(std::size_t size) {
+ constexpr std::size_t max_jpeg_image_size = 0x20000;
+ return static_cast<u32>(std::min(size, max_jpeg_image_size));
+}
+
class IProfile final : public ServiceFramework<IProfile> {
public:
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
@@ -73,32 +101,42 @@ private:
}
void LoadImage(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_ACC, "(STUBBED) called");
- // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
- // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
- constexpr u32 jpeg_size = 107;
- static constexpr std::array<u8, jpeg_size> jpeg{
- 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
- 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
- 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
- 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
- 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
- 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
- 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
- };
- ctx.WriteBuffer(jpeg);
+ LOG_DEBUG(Service_ACC, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(jpeg_size);
+
+ const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+ if (!image.IsOpen()) {
+ LOG_WARNING(Service_ACC,
+ "Failed to load user provided image! Falling back to built-in backup...");
+ ctx.WriteBuffer(backup_jpeg);
+ rb.Push<u32>(backup_jpeg_size);
+ return;
+ }
+
+ const u32 size = SanitizeJPEGSize(image.GetSize());
+ std::vector<u8> buffer(size);
+ image.ReadBytes(buffer.data(), buffer.size());
+
+ ctx.WriteBuffer(buffer.data(), buffer.size());
+ rb.Push<u32>(size);
}
void GetImageSize(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_ACC, "(STUBBED) called");
- constexpr u32 jpeg_size = 107;
+ LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(jpeg_size);
+
+ const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+
+ if (!image.IsOpen()) {
+ LOG_WARNING(Service_ACC,
+ "Failed to load user provided image! Falling back to built-in backup...");
+ rb.Push<u32>(backup_jpeg_size);
+ } else {
+ rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
+ }
}
const ProfileManager& profile_manager;
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index bcb3475db..c08394e4c 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -3,41 +3,66 @@
// Refer to the license.txt file included.
#include <random>
-#include <boost/optional.hpp>
+
+#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
namespace Service::Account {
+
+struct UserRaw {
+ UUID uuid;
+ UUID uuid2;
+ u64 timestamp;
+ ProfileUsername username;
+ INSERT_PADDING_BYTES(0x80);
+};
+static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
+
+struct ProfileDataRaw {
+ INSERT_PADDING_BYTES(0x10);
+ std::array<UserRaw, MAX_USERS> users;
+};
+static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
+
// TODO(ogniK): Get actual error codes
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
-const UUID& UUID::Generate() {
+constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
+
+UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
- uuid[0] = distribution(gen);
- uuid[1] = distribution(gen);
- return *this;
+ return UUID{distribution(gen), distribution(gen)};
}
ProfileManager::ProfileManager() {
- // TODO(ogniK): Create the default user we have for now until loading/saving users is added
- auto user_uuid = UUID{1, 0};
- ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess());
- OpenUser(user_uuid);
+ ParseUserSaveFile();
+
+ if (user_count == 0)
+ CreateNewUser(UUID::Generate(), "yuzu");
+
+ auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
+ if (UserExistsIndex(current))
+ current = 0;
+
+ OpenUser(*GetUser(current));
}
-ProfileManager::~ProfileManager() = default;
+ProfileManager::~ProfileManager() {
+ WriteUserSaveFile();
+}
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
/// internal management of the users profiles
-boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
+std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
if (user_count >= MAX_USERS) {
- return boost::none;
+ return {};
}
- profiles[user_count] = user;
+ profiles[user_count] = profile;
return user_count++;
}
@@ -56,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
/// Helper function to register a user to the system
ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
- if (AddToProfiles(user) == boost::none) {
+ if (!AddToProfiles(user)) {
return ERROR_TOO_MANY_USERS;
}
return RESULT_SUCCESS;
@@ -101,31 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
return CreateNewUser(uuid, username_output);
}
+std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
+ if (index >= MAX_USERS) {
+ return {};
+ }
+
+ return profiles[index].user_uuid;
+}
+
/// Returns a users profile index based on their user id.
-boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
+std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
- return boost::none;
+ return {};
}
- auto iter = std::find_if(profiles.begin(), profiles.end(),
- [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
+
+ const auto iter = std::find_if(profiles.begin(), profiles.end(),
+ [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
if (iter == profiles.end()) {
- return boost::none;
+ return {};
}
+
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
}
/// Returns a users profile index based on their profile
-boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
+std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
return GetUserIndex(user.user_uuid);
}
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
-bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
- ProfileBase& profile) const {
- if (index == boost::none || index >= MAX_USERS) {
+bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
+ if (!index || index >= MAX_USERS) {
return false;
}
- const auto& prof_info = profiles[index.get()];
+ const auto& prof_info = profiles[*index];
profile.user_uuid = prof_info.user_uuid;
profile.username = prof_info.username;
profile.timestamp = prof_info.creation_time;
@@ -134,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
- auto idx = GetUserIndex(uuid);
+ const auto idx = GetUserIndex(uuid);
return GetProfileBase(idx, profile);
}
@@ -161,26 +195,34 @@ std::size_t ProfileManager::GetOpenUserCount() const {
/// Checks if a user id exists in our profile manager
bool ProfileManager::UserExists(UUID uuid) const {
- return (GetUserIndex(uuid) != boost::none);
+ return GetUserIndex(uuid).has_value();
+}
+
+bool ProfileManager::UserExistsIndex(std::size_t index) const {
+ if (index >= MAX_USERS)
+ return false;
+ return profiles[index].user_uuid.uuid != INVALID_UUID;
}
/// Opens a specific user
void ProfileManager::OpenUser(UUID uuid) {
- auto idx = GetUserIndex(uuid);
- if (idx == boost::none) {
+ const auto idx = GetUserIndex(uuid);
+ if (!idx) {
return;
}
- profiles[idx.get()].is_open = true;
+
+ profiles[*idx].is_open = true;
last_opened_user = uuid;
}
/// Closes a specific user
void ProfileManager::CloseUser(UUID uuid) {
- auto idx = GetUserIndex(uuid);
- if (idx == boost::none) {
+ const auto idx = GetUserIndex(uuid);
+ if (!idx) {
return;
}
- profiles[idx.get()].is_open = false;
+
+ profiles[*idx].is_open = false;
}
/// Gets all valid user ids on the system
@@ -210,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
}
/// Return the users profile base and the unknown arbitary data.
-bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
+bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const {
if (GetProfileBase(index, profile)) {
- data = profiles[index.get()].data;
+ data = profiles[*index].data;
return true;
}
return false;
@@ -222,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
ProfileData& data) const {
- auto idx = GetUserIndex(uuid);
+ const auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
@@ -239,4 +281,96 @@ bool ProfileManager::CanSystemRegisterUser() const {
// emulate qlaunch. Update this to dynamically change.
}
+bool ProfileManager::RemoveUser(UUID uuid) {
+ const auto index = GetUserIndex(uuid);
+ if (!index) {
+ return false;
+ }
+
+ profiles[*index] = ProfileInfo{};
+ std::stable_partition(profiles.begin(), profiles.end(),
+ [](const ProfileInfo& profile) { return profile.user_uuid; });
+ return true;
+}
+
+bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
+ const auto index = GetUserIndex(uuid);
+ if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
+ return false;
+ }
+
+ auto& profile = profiles[*index];
+ profile.user_uuid = profile_new.user_uuid;
+ profile.username = profile_new.username;
+ profile.creation_time = profile_new.timestamp;
+
+ return true;
+}
+
+void ProfileManager::ParseUserSaveFile() {
+ FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
+ "rb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
+ "user 'yuzu' with random UUID.");
+ return;
+ }
+
+ ProfileDataRaw data;
+ if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
+ LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
+ "'yuzu' with random UUID.");
+ return;
+ }
+
+ for (std::size_t i = 0; i < MAX_USERS; ++i) {
+ const auto& user = data.users[i];
+
+ if (user.uuid != UUID(INVALID_UUID))
+ AddUser({user.uuid, user.username, user.timestamp, {}, false});
+ }
+
+ std::stable_partition(profiles.begin(), profiles.end(),
+ [](const ProfileInfo& profile) { return profile.user_uuid; });
+}
+
+void ProfileManager::WriteUserSaveFile() {
+ ProfileDataRaw raw{};
+
+ for (std::size_t i = 0; i < MAX_USERS; ++i) {
+ raw.users[i].username = profiles[i].username;
+ raw.users[i].uuid2 = profiles[i].user_uuid;
+ raw.users[i].uuid = profiles[i].user_uuid;
+ raw.users[i].timestamp = profiles[i].creation_time;
+ }
+
+ const auto raw_path =
+ FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
+ if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
+ FileUtil::Delete(raw_path);
+
+ const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
+
+ if (!FileUtil::CreateFullPath(path)) {
+ LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
+ "nand/system/save/8000000000000010/su/avators to mitigate this "
+ "issue.");
+ return;
+ }
+
+ FileUtil::IOFile save(path, "wb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
+ "made in current session will be saved.");
+ return;
+ }
+
+ save.Resize(sizeof(ProfileDataRaw));
+ save.WriteBytes(&raw, sizeof(ProfileDataRaw));
+}
+
}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index bffd4cf4d..747c46c20 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -5,8 +5,8 @@
#pragma once
#include <array>
+#include <optional>
-#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
@@ -36,7 +36,7 @@ struct UUID {
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
- const UUID& Generate();
+ static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
void Invalidate() {
@@ -45,10 +45,20 @@ struct UUID {
std::string Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
+
+ std::string FormatSwitch() const {
+ std::array<u8, 16> s{};
+ std::memcpy(s.data(), uuid.data(), sizeof(u128));
+ return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
+ ":02x}{:02x}{:02x}{:02x}{:02x}",
+ s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
+ s[12], s[13], s[14], s[15]);
+ }
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
-using ProfileUsername = std::array<u8, 0x20>;
+constexpr std::size_t profile_username_size = 32;
+using ProfileUsername = std::array<u8, profile_username_size>;
using ProfileData = std::array<u8, MAX_DATA>;
using UserIDArray = std::array<UUID, MAX_USERS>;
@@ -81,18 +91,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
/// objects
class ProfileManager {
public:
- ProfileManager(); // TODO(ogniK): Load from system save
+ ProfileManager();
~ProfileManager();
ResultCode AddUser(const ProfileInfo& user);
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
ResultCode CreateNewUser(UUID uuid, const std::string& username);
- boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
- boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
- bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const;
+ std::optional<UUID> GetUser(std::size_t index) const;
+ std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
+ std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
+ bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
- bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
+ bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const;
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
@@ -100,6 +111,7 @@ public:
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(UUID uuid) const;
+ bool UserExistsIndex(std::size_t index) const;
void OpenUser(UUID uuid);
void CloseUser(UUID uuid);
UserIDArray GetOpenUsers() const;
@@ -108,11 +120,17 @@ public:
bool CanSystemRegisterUser() const;
+ bool RemoveUser(UUID uuid);
+ bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
+
private:
+ void ParseUserSaveFile();
+ void WriteUserSaveFile();
+ std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
+ bool RemoveProfileAtIndex(std::size_t index);
+
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count = 0;
- boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
- bool RemoveProfileAtIndex(std::size_t index);
UUID last_opened_user{INVALID_UUID};
};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 69bfce1c1..ac3ff9f20 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -4,17 +4,20 @@
#include <array>
#include <cinttypes>
+#include <cstring>
#include <stack>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
+#include "core/hle/service/am/tcap.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -25,14 +28,29 @@
namespace Service::AM {
+constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
+
+struct LaunchParameters {
+ u32_le magic;
+ u32_le is_account_selected;
+ u128 current_user;
+ INSERT_PADDING_BYTES(0x70);
+};
+static_assert(sizeof(LaunchParameters) == 0x88);
+
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CreateWindow"},
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
{11, nullptr, "ReleaseForegroundRights"},
{12, nullptr, "RejectToChangeIntoBackground"},
+ {20, nullptr, "SetAppletWindowVisibility"},
+ {21, nullptr, "SetAppletGpuTimeSlice"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -87,6 +105,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetLastForegroundCaptureImage"},
{1, nullptr, "UpdateLastForegroundCaptureImage"},
@@ -117,7 +136,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
+ // 6.0.0+
+ {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -128,6 +151,7 @@ IDebugFunctions::~IDebugFunctions() = default;
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Exit"},
{1, &ISelfController::LockExit, "LockExit"},
@@ -136,10 +160,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{4, nullptr, "LeaveFatalSection"},
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
- {11, &ISelfController::SetOperationModeChangedNotification,
- "SetOperationModeChangedNotification"},
- {12, &ISelfController::SetPerformanceModeChangedNotification,
- "SetPerformanceModeChangedNotification"},
+ {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
+ {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
{15, nullptr, "SetScreenShotAppletIdentityInfo"},
@@ -165,7 +187,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{69, nullptr, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
{80, nullptr, "SetWirelessPriorityMode"},
+ {90, nullptr, "GetAccumulatedSuspendedTickValue"},
+ {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
+ {1000, nullptr, "GetDebugStorageChannel"},
};
+ // clang-format on
+
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
@@ -312,6 +339,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
}
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
@@ -336,11 +364,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
{52, nullptr, "SwitchLcdBacklight"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
- {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent,
- "GetDefaultDisplayResolutionChangeEvent"},
+ {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
{62, nullptr, "GetHdcpAuthenticationState"},
{63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
};
+ // clang-format on
+
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
@@ -432,11 +461,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{10, &IStorageAccessor::Write, "Write"},
{11, &IStorageAccessor::Read, "Read"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -489,10 +521,13 @@ class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
{1, nullptr, "OpenTransferStorage"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -512,6 +547,7 @@ private:
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
{1, nullptr, "IsCompleted"},
@@ -532,6 +568,8 @@ public:
{150, nullptr, "RequestForAppletToGetForeground"},
{160, nullptr, "GetIndirectLayerConsumerHandle"},
};
+ // clang-format on
+
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
@@ -624,13 +662,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
}
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
+ // clang-format off
static const FunctionInfo functions[] = {
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
{10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
{11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
{12, nullptr, "CreateApplicationAndRequestToStart"},
- {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest,
- "CreateApplicationAndRequestToStartForQuest"},
+ {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -638,10 +676,10 @@ 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"},
@@ -664,26 +702,57 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{1000, nullptr, "CreateMovieMaker"},
{1001, nullptr, "PrepareForJit"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
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
- 1, 0, 0, 0, // IsAccountSelected (bool)
- 1, 0, 0, 0, // User Id (word 0)
- 0, 0, 0, 0, // User Id (word 1)
- 0, 0, 0, 0, // User Id (word 2)
- 0, 0, 0, 0 // User Id (word 3)
- }};
+ LaunchParameters params{};
- std::vector<u8> buffer(data.begin(), data.end());
+ params.magic = POP_LAUNCH_PARAMETER_MAGIC;
+ params.is_account_selected = 1;
+
+ Account::ProfileManager profile_manager{};
+ const auto uuid = profile_manager.GetUser(Settings::values.current_user);
+ ASSERT(uuid);
+ params.current_user = uuid->uuid;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
+
+ std::vector<u8> buffer(sizeof(LaunchParameters));
+ std::memcpy(buffer.data(), &params, buffer.size());
+
rb.PushIpcInterface<AM::IStorage>(buffer);
LOG_DEBUG(Service_AM, "called");
@@ -776,9 +845,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
std::make_shared<IdleSys>()->InstallAsService(service_manager);
std::make_shared<OMM>()->InstallAsService(service_manager);
std::make_shared<SPSM>()->InstallAsService(service_manager);
+ std::make_shared<TCAP>()->InstallAsService(service_manager);
}
IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
+ // clang-format off
static const FunctionInfo functions[] = {
{10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
{11, nullptr, "LockForeground"},
@@ -787,7 +858,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
{21, nullptr, "GetPopFromGeneralChannelEvent"},
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
{31, nullptr, "GetWriterLockAccessorEx"},
+ {100, nullptr, "PopRequestLaunchApplicationForDebug"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -800,6 +874,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
}
IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "RequestToEnterSleep"},
{1, nullptr, "EnterSleep"},
@@ -813,18 +888,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
{14, nullptr, "ShouldSleepOnBoot"},
{15, nullptr, "GetHdcpAuthenticationFailedEvent"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
IGlobalStateController::~IGlobalStateController() = default;
IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CreateApplication"},
{1, nullptr, "PopLaunchRequestedApplication"},
{10, nullptr, "CreateSystemApplication"},
{100, nullptr, "PopFloatingApplicationForDevelopment"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -832,6 +912,7 @@ IApplicationCreator::~IApplicationCreator() = default;
IProcessWindingController::IProcessWindingController()
: ServiceFramework("IProcessWindingController") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetLaunchReason"},
{11, nullptr, "OpenCallingLibraryApplet"},
@@ -842,6 +923,8 @@ IProcessWindingController::IProcessWindingController()
{40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
{41, nullptr, "ReserveToStartAndWait"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
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/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 4296c255e..68ea778e8 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
+ // clang-format off
static const FunctionInfo functions[] = {
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
{200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
@@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
{300, nullptr, "OpenOverlayAppletProxy"},
{350, nullptr, "OpenSystemApplicationProxy"},
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
+ {401, nullptr, "GetSystemAppletControllerForDebug"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index e45cf6e20..60717afd9 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
@@ -25,6 +26,8 @@ public:
{20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
{1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp
index 0e3088bc8..f814fe2c0 100644
--- a/src/core/hle/service/am/idle.cpp
+++ b/src/core/hle/service/am/idle.cpp
@@ -12,9 +12,9 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} {
{0, nullptr, "GetAutoPowerDownEvent"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
- {3, nullptr, "Unknown3"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "Unknown5"},
+ {3, nullptr, "SetHandlingContext"},
+ {4, nullptr, "LoadAndApplySettings"},
+ {5, nullptr, "ReportUserIsActive"},
};
// clang-format on
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/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp
new file mode 100644
index 000000000..a75cbdda8
--- /dev/null
+++ b/src/core/hle/service/am/tcap.cpp
@@ -0,0 +1,23 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/am/tcap.h"
+
+namespace Service::AM {
+
+TCAP::TCAP() : ServiceFramework{"tcap"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetContinuousHighSkinTemperatureEvent"},
+ {1, nullptr, "SetOperationMode"},
+ {2, nullptr, "LoadAndApplySettings"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+TCAP::~TCAP() = default;
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h
new file mode 100644
index 000000000..2021b55d1
--- /dev/null
+++ b/src/core/hle/service/am/tcap.h
@@ -0,0 +1,17 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::AM {
+
+class TCAP final : public ServiceFramework<TCAP> {
+public:
+ explicit TCAP();
+ ~TCAP() override;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 79580bcd9..54305cf05 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,10 +22,10 @@
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;
+static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
+ return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
}
static std::vector<u64> AccumulateAOCTitleIDs() {
@@ -53,21 +56,25 @@ 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;
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 4};
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
- rb.Push<u32>(std::count_if(add_on_content.begin(), add_on_content.end(), [&current](u64 tid) {
- return (tid & DLC_BASE_TITLE_ID_MASK) == current;
- }));
+ rb.Push<u32>(static_cast<u32>(
+ std::count_if(add_on_content.begin(), add_on_content.end(),
+ [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
}
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
@@ -91,20 +98,30 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
return;
}
- count = std::min<size_t>(out.size() - offset, count);
+ count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
std::rotate(out.begin(), out.begin() + offset, out.end());
out.resize(count);
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/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index fc6067e59..783c39503 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -2,8 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <chrono>
#include <cstring>
#include <memory>
+#include <optional>
#include <vector>
#include <opus.h>
@@ -33,7 +35,8 @@ public:
{1, nullptr, "SetContext"},
{2, nullptr, "DecodeInterleavedForMultiStream"},
{3, nullptr, "SetContextForMultiStream"},
- {4, nullptr, "Unknown4"},
+ {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
+ "DecodeInterleavedWithPerformance"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
@@ -59,8 +62,31 @@ private:
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
- bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
- std::vector<opus_int16>& output) {
+ void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
+ u32 consumed = 0;
+ u32 sample_count = 0;
+ u64 performance = 0;
+ std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
+ if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
+ performance)) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(ogniK): Use correct error code
+ rb.Push(ResultCode(-1));
+ return;
+ }
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(consumed);
+ rb.Push<u64>(performance);
+ rb.Push<u32>(sample_count);
+ ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
+ }
+
+ bool Decoder_DecodeInterleaved(
+ u32& consumed, u32& sample_count, const std::vector<u8>& input,
+ std::vector<opus_int16>& output,
+ std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
+ const auto start_time = std::chrono::high_resolution_clock::now();
std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
if (sizeof(OpusHeader) > input.size())
return false;
@@ -80,8 +106,13 @@ private:
(static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
if (out_sample_count < 0)
return false;
+ const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
sample_count = out_sample_count;
consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
+ if (performance_time.has_value()) {
+ performance_time->get() =
+ std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
+ }
return true;
}
@@ -130,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
std::size_t worker_sz = WorkerBufferSize(channel_count);
- ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
+ ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
static_cast<OpusDecoder*>(operator new(worker_sz))};
if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
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 aed2abb71..e32a7c48e 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -264,6 +264,15 @@ ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
return RESULT_SUCCESS;
}
+void SetPackedUpdate(FileSys::VirtualFile update_raw) {
+ LOG_TRACE(Service_FS, "Setting packed update for romfs");
+
+ if (romfs_factory == nullptr)
+ return;
+
+ romfs_factory->SetPackedUpdate(std::move(update_raw));
+}
+
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
LOG_TRACE(Service_FS, "Opening RomFS for current process");
@@ -310,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)
@@ -325,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)
@@ -334,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)
@@ -352,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);
@@ -374,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 7039a2247..6ca5c5636 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -39,6 +39,7 @@ ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory)
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
+void SetPackedUpdate(FileSys::VirtualFile update_raw);
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type);
@@ -46,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/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d5dced429..c1c83a11d 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -17,6 +17,7 @@
#include "core/file_sys/errors.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/patch_manager.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
@@ -272,8 +273,8 @@ public:
{0, &IFileSystem::CreateFile, "CreateFile"},
{1, &IFileSystem::DeleteFile, "DeleteFile"},
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
- {3, nullptr, "DeleteDirectory"},
- {4, nullptr, "DeleteDirectoryRecursively"},
+ {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
+ {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
{5, &IFileSystem::RenameFile, "RenameFile"},
{6, nullptr, "RenameDirectory"},
{7, &IFileSystem::GetEntryType, "GetEntryType"},
@@ -328,6 +329,30 @@ public:
rb.Push(backend.CreateDirectory(name));
}
+ void DeleteDirectory(Kernel::HLERequestContext& ctx) {
+ const IPC::RequestParser rp{ctx};
+
+ const auto file_buffer = ctx.ReadBuffer();
+ std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called directory {}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.DeleteDirectory(name));
+ }
+
+ void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
+ const IPC::RequestParser rp{ctx};
+
+ const auto file_buffer = ctx.ReadBuffer();
+ std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called directory {}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.DeleteDirectoryRecursively(name));
+ }
+
void RenameFile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -630,6 +655,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
static_cast<u8>(storage_id), unknown, title_id);
auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
+
if (data.Failed()) {
// TODO(DarkLordZach): Find the right error code to use here
LOG_ERROR(Service_FS,
@@ -640,7 +666,9 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
return;
}
- IStorage storage(std::move(data.Unwrap()));
+ FileSys::PatchManager pm{title_id};
+
+ IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
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..4b4d1324f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -0,0 +1,620 @@
+// 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 u32 NPAD_HANDHELD = 32;
+constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
+constexpr u32 MAX_NPAD_ID = 7;
+constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
+ Controller_NPad::NPadControllerType::JoyDual;
+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;
+ }
+
+ 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(PREFERRED_CONTROLLER);
+ }
+}
+
+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);
+
+ if (controller_type == NPadControllerType::JoyLeft ||
+ controller_type == NPadControllerType::JoyRight) {
+ if (npad.properties.is_horizontal) {
+ ControllerPadState state{};
+ AnalogPosition temp_lstick_entry{};
+ AnalogPosition temp_rstick_entry{};
+ if (controller_type == NPadControllerType::JoyLeft) {
+ state.d_down.Assign(pad_state.d_left.Value());
+ state.d_left.Assign(pad_state.d_up.Value());
+ state.d_right.Assign(pad_state.d_down.Value());
+ state.d_up.Assign(pad_state.d_right.Value());
+ state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
+ state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
+
+ state.zl.Assign(pad_state.zl.Value());
+ state.plus.Assign(pad_state.minus.Value());
+
+ temp_lstick_entry = lstick_entry;
+ temp_rstick_entry = rstick_entry;
+ std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
+ std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
+ temp_lstick_entry.y *= -1;
+ } else if (controller_type == NPadControllerType::JoyRight) {
+ state.x.Assign(pad_state.a.Value());
+ state.a.Assign(pad_state.b.Value());
+ state.b.Assign(pad_state.y.Value());
+ state.y.Assign(pad_state.b.Value());
+
+ state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
+ state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
+ state.zr.Assign(pad_state.zr.Value());
+ state.plus.Assign(pad_state.plus.Value());
+
+ temp_lstick_entry = lstick_entry;
+ temp_rstick_entry = rstick_entry;
+ std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
+ std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
+ temp_rstick_entry.x *= -1;
+ }
+ pad_state.raw = state.raw;
+ lstick_entry = temp_lstick_entry;
+ rstick_entry = temp_rstick_entry;
+ }
+ }
+
+ 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);
+ for (std::size_t i = 0; i < connected_controllers.size(); i++) {
+ auto& controller = connected_controllers[i];
+ if (!controller.is_connected) {
+ continue;
+ }
+ if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
+ controller.type = DecideBestController(PREFERRED_CONTROLLER);
+ InitNewlyAddedControler(i);
+ }
+ }
+}
+
+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 == NPAD_HANDHELD) {
+ controller_pos = 8;
+ }
+ // Unknown controller conversion
+ if (controller_pos == NPAD_UNKNOWN) {
+ 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;
+}
+
+bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
+ const bool support_handheld =
+ std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
+ supported_npad_id_types.end();
+ if (controller == NPadControllerType::Handheld) {
+ // Handheld is not even a supported type, lets stop here
+ if (!support_handheld) {
+ return false;
+ }
+ // Handheld should not be supported in docked mode
+ if (Settings::values.use_docked_mode) {
+ return false;
+ }
+
+ return true;
+ }
+ if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
+ [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
+ switch (controller) {
+ case NPadControllerType::ProController:
+ return style.pro_controller;
+ case NPadControllerType::JoyDual:
+ return style.joycon_dual;
+ case NPadControllerType::JoyLeft:
+ return style.joycon_left;
+ case NPadControllerType::JoyRight:
+ return style.joycon_right;
+ case NPadControllerType::Pokeball:
+ return style.pokeball;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
+ NPadControllerType priority) const {
+ if (IsControllerSupported(priority)) {
+ return priority;
+ }
+ const auto is_docked = Settings::values.use_docked_mode;
+ if (is_docked && priority == NPadControllerType::Handheld) {
+ priority = NPadControllerType::JoyDual;
+ if (IsControllerSupported(priority)) {
+ return priority;
+ }
+ }
+ std::vector<NPadControllerType> priority_list;
+ switch (priority) {
+ case NPadControllerType::ProController:
+ priority_list.push_back(NPadControllerType::JoyDual);
+ if (!is_docked) {
+ priority_list.push_back(NPadControllerType::Handheld);
+ }
+ priority_list.push_back(NPadControllerType::JoyLeft);
+ priority_list.push_back(NPadControllerType::JoyRight);
+ priority_list.push_back(NPadControllerType::Pokeball);
+ break;
+ case NPadControllerType::Handheld:
+ priority_list.push_back(NPadControllerType::JoyDual);
+ priority_list.push_back(NPadControllerType::ProController);
+ priority_list.push_back(NPadControllerType::JoyLeft);
+ priority_list.push_back(NPadControllerType::JoyRight);
+ priority_list.push_back(NPadControllerType::Pokeball);
+ break;
+ case NPadControllerType::JoyDual:
+ if (!is_docked) {
+ priority_list.push_back(NPadControllerType::Handheld);
+ }
+ priority_list.push_back(NPadControllerType::ProController);
+ priority_list.push_back(NPadControllerType::JoyLeft);
+ priority_list.push_back(NPadControllerType::JoyRight);
+ priority_list.push_back(NPadControllerType::Pokeball);
+ break;
+ case NPadControllerType::JoyLeft:
+ priority_list.push_back(NPadControllerType::JoyRight);
+ priority_list.push_back(NPadControllerType::JoyDual);
+ if (!is_docked) {
+ priority_list.push_back(NPadControllerType::Handheld);
+ }
+ priority_list.push_back(NPadControllerType::ProController);
+ priority_list.push_back(NPadControllerType::Pokeball);
+ break;
+ case NPadControllerType::JoyRight:
+ priority_list.push_back(NPadControllerType::JoyLeft);
+ priority_list.push_back(NPadControllerType::JoyDual);
+ if (!is_docked) {
+ priority_list.push_back(NPadControllerType::Handheld);
+ }
+ priority_list.push_back(NPadControllerType::ProController);
+ priority_list.push_back(NPadControllerType::Pokeball);
+ break;
+ case NPadControllerType::Pokeball:
+ priority_list.push_back(NPadControllerType::JoyLeft);
+ priority_list.push_back(NPadControllerType::JoyRight);
+ priority_list.push_back(NPadControllerType::JoyDual);
+ if (!is_docked) {
+ priority_list.push_back(NPadControllerType::Handheld);
+ }
+ priority_list.push_back(NPadControllerType::ProController);
+ break;
+ default:
+ priority_list.push_back(NPadControllerType::JoyDual);
+ if (!is_docked) {
+ priority_list.push_back(NPadControllerType::Handheld);
+ }
+ priority_list.push_back(NPadControllerType::ProController);
+ priority_list.push_back(NPadControllerType::JoyLeft);
+ priority_list.push_back(NPadControllerType::JoyRight);
+ priority_list.push_back(NPadControllerType::JoyDual);
+ }
+
+ const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
+ [this](auto type) { return IsControllerSupported(type); });
+ if (iter == priority_list.end()) {
+ UNIMPLEMENTED_MSG("Could not find supported controller!");
+ return priority;
+ }
+
+ return *iter;
+}
+
+} // 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..ac86985ff
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -0,0 +1,289 @@
+// 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);
+ bool IsControllerSupported(NPadControllerType controller) const;
+ NPadControllerType DecideBestController(NPadControllerType priority) const;
+};
+} // 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..a9aa9ec78 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
@@ -818,6 +827,7 @@ public:
{11, nullptr, "EnableJoyPollingReceiveMode"},
{12, nullptr, "DisableJoyPollingReceiveMode"},
{13, nullptr, "GetPollingData"},
+ {14, nullptr, "SetStatusManagerType"},
};
// 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/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index 7321584e1..164c57e18 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -18,35 +18,35 @@ public:
explicit LBL() : ServiceFramework{"lbl"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "GetCurrentBacklightLevel"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "GetAlsComputedBacklightLevel"},
- {6, nullptr, "TurnOffBacklight"},
- {7, nullptr, "TurnOnBacklight"},
- {8, nullptr, "GetBacklightStatus"},
- {9, nullptr, "Unknown5"},
- {10, nullptr, "Unknown6"},
- {11, nullptr, "Unknown7"},
- {12, nullptr, "Unknown8"},
- {13, nullptr, "Unknown9"},
- {14, nullptr, "Unknown10"},
- {15, nullptr, "GetAutoBrightnessSetting"},
- {16, nullptr, "ReadRawLightSensor"},
- {17, nullptr, "Unknown11"},
- {18, nullptr, "Unknown12"},
- {19, nullptr, "Unknown13"},
- {20, nullptr, "Unknown14"},
- {21, nullptr, "Unknown15"},
- {22, nullptr, "Unknown16"},
- {23, nullptr, "Unknown17"},
- {24, nullptr, "Unknown18"},
- {25, nullptr, "Unknown19"},
+ {0, nullptr, "SaveCurrentSetting"},
+ {1, nullptr, "LoadCurrentSetting"},
+ {2, nullptr, "SetCurrentBrightnessSetting"},
+ {3, nullptr, "GetCurrentBrightnessSetting"},
+ {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
+ {5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
+ {6, nullptr, "SwitchBacklightOn"},
+ {7, nullptr, "SwitchBacklightOff"},
+ {8, nullptr, "GetBacklightSwitchStatus"},
+ {9, nullptr, "EnableDimming"},
+ {10, nullptr, "DisableDimming"},
+ {11, nullptr, "IsDimmingEnabled"},
+ {12, nullptr, "EnableAutoBrightnessControl"},
+ {13, nullptr, "DisableAutoBrightnessControl"},
+ {14, nullptr, "IsAutoBrightnessControlEnabled"},
+ {15, nullptr, "SetAmbientLightSensorValue"},
+ {16, nullptr, "GetAmbientLightSensorValue"},
+ {17, nullptr, "SetBrightnessReflectionDelayLevel"},
+ {18, nullptr, "GetBrightnessReflectionDelayLevel"},
+ {19, nullptr, "SetCurrentBrightnessMapping"},
+ {20, nullptr, "GetCurrentBrightnessMapping"},
+ {21, nullptr, "SetCurrentAmbientLightSensorMapping"},
+ {22, nullptr, "GetCurrentAmbientLightSensorMapping"},
+ {23, nullptr, "IsAmbientLightSensorAvailable"},
+ {24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
+ {25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
{26, &LBL::EnableVrMode, "EnableVrMode"},
{27, &LBL::DisableVrMode, "DisableVrMode"},
- {28, &LBL::GetVrMode, "GetVrMode"},
+ {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
};
// clang-format on
@@ -72,7 +72,7 @@ private:
LOG_DEBUG(Service_LBL, "called");
}
- void GetVrMode(Kernel::HLERequestContext& ctx) {
+ void IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(vr_mode_enabled);
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index ec32faf15..d607d985e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -3,9 +3,13 @@
// Refer to the license.txt file included.
#include <memory>
+#include <fmt/format.h>
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/service.h"
+#include "core/loader/nro.h"
namespace Service::LDR {
@@ -59,16 +63,58 @@ public:
explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "LoadNro"},
+ {0, &RelocatableObject::LoadNro, "LoadNro"},
{1, nullptr, "UnloadNro"},
- {2, nullptr, "LoadNrr"},
+ {2, &RelocatableObject::LoadNrr, "LoadNrr"},
{3, nullptr, "UnloadNrr"},
- {4, nullptr, "Initialize"},
+ {4, &RelocatableObject::Initialize, "Initialize"},
};
// clang-format on
RegisterHandlers(functions);
}
+
+ void LoadNrr(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_LDR, "(STUBBED) called");
+ }
+
+ void LoadNro(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const VAddr nro_addr{rp.Pop<VAddr>()};
+ const u64 nro_size{rp.Pop<u64>()};
+ const VAddr bss_addr{rp.Pop<VAddr>()};
+ const u64 bss_size{rp.Pop<u64>()};
+
+ // Read NRO data from memory
+ std::vector<u8> nro_data(nro_size);
+ Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
+
+ // Load NRO as new executable module
+ const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
+ Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
+
+ // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
+ // It is currently missing:
+ // - Signature checks with LoadNRR
+ // - Checking if a module has already been loaded
+ // - Using/validating BSS, etc. params (these are used from NRO header instead)
+ // - Error checking
+ // - ...Probably other things
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(addr);
+ LOG_WARNING(Service_LDR, "(STUBBED) called");
+ }
+
+ void Initialize(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_LDR, "(STUBBED) called");
+ }
};
void InstallInterfaces(SM::ServiceManager& sm) {
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/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 8fec97db8..30e542542 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -10,12 +10,13 @@
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
+#include "core/settings.h"
namespace Service::NFC {
class IAm final : public ServiceFramework<IAm> {
public:
- explicit IAm() : ServiceFramework{"IAm"} {
+ explicit IAm() : ServiceFramework{"NFC::IAm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
@@ -52,7 +53,7 @@ private:
class MFIUser final : public ServiceFramework<MFIUser> {
public:
- explicit MFIUser() : ServiceFramework{"IUser"} {
+ explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
@@ -100,13 +101,13 @@ private:
class IUser final : public ServiceFramework<IUser> {
public:
- explicit IUser() : ServiceFramework{"IUser"} {
+ explicit IUser() : ServiceFramework{"NFC::IUser"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Initialize"},
- {1, nullptr, "Finalize"},
- {2, nullptr, "GetState"},
- {3, nullptr, "IsNfcEnabled"},
+ {0, &IUser::InitializeOld, "InitializeOld"},
+ {1, &IUser::FinalizeOld, "FinalizeOld"},
+ {2, &IUser::GetStateOld, "GetStateOld"},
+ {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
{400, nullptr, "Initialize"},
{401, nullptr, "Finalize"},
{402, nullptr, "GetState"},
@@ -130,11 +131,47 @@ public:
RegisterHandlers(functions);
}
+
+private:
+ enum class NfcStates : u32 {
+ Finalized = 6,
+ };
+
+ void InitializeOld(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0};
+ rb.Push(RESULT_SUCCESS);
+
+ // We don't deal with hardware initialization so we can just stub this.
+ LOG_DEBUG(Service_NFC, "called");
+ }
+
+ void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u8>(Settings::values.enable_nfc);
+
+ LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
+ }
+
+ void GetStateOld(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NFC, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
+ }
+
+ void FinalizeOld(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NFC, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
};
class NFC_U final : public ServiceFramework<NFC_U> {
public:
- explicit NFC_U() : ServiceFramework{"nfc:u"} {
+ explicit NFC_U() : ServiceFramework{"nfc:user"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 8c07a05c2..c1af878fe 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,56 +2,67 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <atomic>
+
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
+#include "core/hle/lock.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
namespace Service::NFP {
+namespace ErrCodes {
+constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
+ -1); // TODO(ogniK): Find the actual error code
+}
+
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
- : ServiceFramework(name), module(std::move(module)) {}
+ : ServiceFramework(name), module(std::move(module)) {
+ auto& kernel = Core::System::GetInstance().Kernel();
+ nfc_tag_load =
+ Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected");
+}
Module::Interface::~Interface() = default;
class IUser final : public ServiceFramework<IUser> {
public:
- IUser() : ServiceFramework("IUser") {
+ IUser(Module::Interface& nfp_interface)
+ : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
- {1, nullptr, "Finalize"},
+ {1, &IUser::Finalize, "Finalize"},
{2, &IUser::ListDevices, "ListDevices"},
- {3, nullptr, "StartDetection"},
- {4, nullptr, "StopDetection"},
- {5, nullptr, "Mount"},
- {6, nullptr, "Unmount"},
- {7, nullptr, "OpenApplicationArea"},
- {8, nullptr, "GetApplicationArea"},
+ {3, &IUser::StartDetection, "StartDetection"},
+ {4, &IUser::StopDetection, "StopDetection"},
+ {5, &IUser::Mount, "Mount"},
+ {6, &IUser::Unmount, "Unmount"},
+ {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
+ {8, &IUser::GetApplicationArea, "GetApplicationArea"},
{9, nullptr, "SetApplicationArea"},
{10, nullptr, "Flush"},
{11, nullptr, "Restore"},
{12, nullptr, "CreateApplicationArea"},
- {13, nullptr, "GetTagInfo"},
- {14, nullptr, "GetRegisterInfo"},
- {15, nullptr, "GetCommonInfo"},
- {16, nullptr, "GetModelInfo"},
+ {13, &IUser::GetTagInfo, "GetTagInfo"},
+ {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
+ {15, &IUser::GetCommonInfo, "GetCommonInfo"},
+ {16, &IUser::GetModelInfo, "GetModelInfo"},
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
{19, &IUser::GetState, "GetState"},
{20, &IUser::GetDeviceState, "GetDeviceState"},
{21, &IUser::GetNpadId, "GetNpadId"},
- {22, nullptr, "GetApplicationArea2"},
+ {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{24, nullptr, "RecreateApplicationArea"},
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- activate_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");
deactivate_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
@@ -59,6 +70,17 @@ public:
}
private:
+ struct TagInfo {
+ std::array<u8, 10> uuid;
+ u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
+ // mean something else
+ INSERT_PADDING_BYTES(0x15);
+ u32_le protocol;
+ u32_le tag_type;
+ INSERT_PADDING_BYTES(0x2c);
+ };
+ static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
+
enum class State : u32 {
NonInitialized = 0,
Initialized = 1,
@@ -66,15 +88,40 @@ private:
enum class DeviceState : u32 {
Initialized = 0,
+ SearchingForTag = 1,
+ TagFound = 2,
+ TagRemoved = 3,
+ TagNearby = 4,
+ Unknown5 = 5,
+ Finalized = 6
+ };
+
+ struct CommonInfo {
+ u16_be last_write_year;
+ u8 last_write_month;
+ u8 last_write_day;
+ u16_be write_counter;
+ u16_be version;
+ u32_be application_area_size;
+ INSERT_PADDING_BYTES(0x34);
};
+ static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
void Initialize(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2, 0};
+ rb.Push(RESULT_SUCCESS);
state = State::Initialized;
- IPC::ResponseBuilder rb{ctx, 2};
+ LOG_DEBUG(Service_NFC, "called");
+ }
+
+ void GetState(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 3, 0};
rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(static_cast<u32>(state));
+
+ LOG_DEBUG(Service_NFC, "called");
}
void ListDevices(Kernel::HLERequestContext& ctx) {
@@ -83,80 +130,219 @@ private:
ctx.WriteBuffer(&device_handle, sizeof(device_handle));
- LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size);
+ LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
+ rb.Push<u32>(1);
}
- void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
+ void GetNpadId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
- LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
+ LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(npad_id);
+ }
+ void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 dev_handle = rp.Pop<u64>();
+ LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(activate_event);
+ rb.PushCopyObjects(nfp_interface.GetNFCEvent());
+ has_attached_handle = true;
}
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
- LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
+ LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(deactivate_event);
}
- void GetState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
+ void StopDetection(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+ switch (device_state) {
+ case DeviceState::TagFound:
+ case DeviceState::TagNearby:
+ deactivate_event->Signal();
+ device_state = DeviceState::Initialized;
+ break;
+ case DeviceState::SearchingForTag:
+ case DeviceState::TagRemoved:
+ device_state = DeviceState::Initialized;
+ break;
+ }
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(static_cast<u32>(state));
}
void GetDeviceState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
+ LOG_DEBUG(Service_NFP, "called");
+ auto nfc_event = nfp_interface.GetNFCEvent();
+ if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) {
+ device_state = DeviceState::TagFound;
+ nfc_event->Clear();
+ }
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(device_state));
}
- void GetNpadId(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 dev_handle = rp.Pop<u64>();
- LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
- IPC::ResponseBuilder rb{ctx, 3};
+ void StartDetection(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
+ device_state = DeviceState::SearchingForTag;
+ }
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetTagInfo(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ auto amiibo = nfp_interface.GetAmiiboBuffer();
+ TagInfo tag_info{};
+ std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size()));
+ tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
+
+ tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
+ tag_info.tag_type = 2;
+ ctx.WriteBuffer(&tag_info, sizeof(TagInfo));
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Mount(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ device_state = DeviceState::TagNearby;
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetModelInfo(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ auto amiibo = nfp_interface.GetAmiiboBuffer();
+ ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Unmount(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ device_state = DeviceState::TagFound;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Finalize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+
+ device_state = DeviceState::Finalized;
+
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(npad_id);
}
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 dev_handle = rp.Pop<u64>();
- LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
+ LOG_WARNING(Service_NFP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(availability_change_event);
}
- const u64 device_handle{0xDEAD};
- const HID::ControllerID npad_id{HID::Controller_Player1};
+ void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NFP, "(STUBBED) called");
+
+ // TODO(ogniK): Pull Mii and owner data from amiibo
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetCommonInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NFP, "(STUBBED) called");
+
+ // TODO(ogniK): Pull common information from amiibo
+
+ CommonInfo common_info{};
+ common_info.application_area_size = 0;
+ ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFP, "called");
+ // We don't need to worry about this since we can just open the file
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NFP, "(STUBBED) called");
+ // We don't need to worry about this since we can just open the file
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
+ }
+
+ void GetApplicationArea(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NFP, "(STUBBED) called");
+
+ // TODO(ogniK): Pull application area from amiibo
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
+ }
+
+ bool has_attached_handle{};
+ const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')};
+ const u32 npad_id{0}; // Player 1 controller
State state{State::NonInitialized};
DeviceState device_state{DeviceState::Initialized};
- Kernel::SharedPtr<Kernel::Event> activate_event;
Kernel::SharedPtr<Kernel::Event> deactivate_event;
Kernel::SharedPtr<Kernel::Event> availability_change_event;
+ const Module::Interface& nfp_interface;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IUser>();
+ rb.PushIpcInterface<IUser>(*this);
+}
+
+bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ if (buffer.size() < sizeof(AmiiboFile)) {
+ return false;
+ }
+
+ std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
+ nfc_tag_load->Signal();
+ return true;
+}
+const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
+ return nfc_tag_load;
+}
+const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
+ return amiibo;
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 77df343c4..5c0ae8a54 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -4,6 +4,9 @@
#pragma once
+#include <array>
+#include <vector>
+#include "core/hle/kernel/event.h"
#include "core/hle/service/service.h"
namespace Service::NFP {
@@ -15,7 +18,27 @@ public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
~Interface() override;
+ struct ModelInfo {
+ std::array<u8, 0x8> amiibo_identification_block;
+ INSERT_PADDING_BYTES(0x38);
+ };
+ static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
+
+ struct AmiiboFile {
+ std::array<u8, 10> uuid;
+ INSERT_PADDING_BYTES(0x4a);
+ ModelInfo model_info;
+ };
+ static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
+
void CreateUserInterface(Kernel::HLERequestContext& ctx);
+ bool LoadAmiibo(const std::vector<u8>& buffer);
+ const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
+ const AmiiboFile& GetAmiiboBuffer() const;
+
+ private:
+ Kernel::SharedPtr<Kernel::Event> nfc_tag_load{};
+ AmiiboFile amiibo{};
protected:
std::shared_ptr<Module> module;
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/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
new file mode 100644
index 000000000..ccb6f9da9
--- /dev/null
+++ b/src/core/hle/service/npns/npns.cpp
@@ -0,0 +1,88 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "core/hle/service/npns/npns.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::NPNS {
+
+class NPNS_S final : public ServiceFramework<NPNS_S> {
+public:
+ explicit NPNS_S() : ServiceFramework{"npns:s"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, nullptr, "ListenAll"},
+ {2, nullptr, "ListenTo"},
+ {3, nullptr, "Receive"},
+ {4, nullptr, "ReceiveRaw"},
+ {5, nullptr, "GetReceiveEvent"},
+ {6, nullptr, "ListenUndelivered"},
+ {7, nullptr, "GetStateChangeEVent"},
+ {11, nullptr, "SubscribeTopic"},
+ {12, nullptr, "UnsubscribeTopic"},
+ {13, nullptr, "QueryIsTopicExist"},
+ {21, nullptr, "CreateToken"},
+ {22, nullptr, "CreateTokenWithApplicationId"},
+ {23, nullptr, "DestroyToken"},
+ {24, nullptr, "DestroyTokenWithApplicationId"},
+ {25, nullptr, "QueryIsTokenValid"},
+ {31, nullptr, "UploadTokenToBaaS"},
+ {32, nullptr, "DestroyTokenForBaaS"},
+ {33, nullptr, "CreateTokenForBaaS"},
+ {34, nullptr, "SetBaaSDeviceAccountIdList"},
+ {101, nullptr, "Suspend"},
+ {102, nullptr, "Resume"},
+ {103, nullptr, "GetState"},
+ {104, nullptr, "GetStatistics"},
+ {105, nullptr, "GetPlayReportRequestEvent"},
+ {111, nullptr, "GetJid"},
+ {112, nullptr, "CreateJid"},
+ {113, nullptr, "DestroyJid"},
+ {114, nullptr, "AttachJid"},
+ {115, nullptr, "DetachJid"},
+ {201, nullptr, "RequestChangeStateForceTimed"},
+ {102, nullptr, "RequestChangeStateForceAsync"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class NPNS_U final : public ServiceFramework<NPNS_U> {
+public:
+ explicit NPNS_U() : ServiceFramework{"npns:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, nullptr, "ListenAll"},
+ {2, nullptr, "ListenTo"},
+ {3, nullptr, "Receive"},
+ {4, nullptr, "ReceiveRaw"},
+ {5, nullptr, "GetReceiveEvent"},
+ {7, nullptr, "GetStateChangeEVent"},
+ {21, nullptr, "CreateToken"},
+ {23, nullptr, "DestroyToken"},
+ {25, nullptr, "QueryIsTokenValid"},
+ {26, nullptr, "ListenToMyApplicationId"},
+ {101, nullptr, "Suspend"},
+ {102, nullptr, "Resume"},
+ {103, nullptr, "GetState"},
+ {104, nullptr, "GetStatistics"},
+ {111, nullptr, "GetJid"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& sm) {
+ std::make_shared<NPNS_S>()->InstallAsService(sm);
+ std::make_shared<NPNS_U>()->InstallAsService(sm);
+}
+
+} // namespace Service::NPNS
diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h
new file mode 100644
index 000000000..861cd3e48
--- /dev/null
+++ b/src/core/hle/service/npns/npns.h
@@ -0,0 +1,15 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::NPNS {
+
+void InstallInterfaces(SM::ServiceManager& sm);
+
+} // namespace Service::NPNS
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/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index d8b8037a8..c41ef7058 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -15,6 +15,11 @@
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
+namespace NvErrCodes {
+enum {
+ InvalidNmapHandle = -22,
+};
+}
nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu::~nvhost_as_gpu() = default;
@@ -79,14 +84,16 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
std::memcpy(entries.data(), input.data(), input.size());
auto& gpu = Core::System::GetInstance().GPU();
-
for (const auto& entry : entries) {
LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
-
auto object = nvmap_dev->GetObject(entry.nvmap_handle);
- ASSERT(object);
+ if (!object) {
+ LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
+ std::memcpy(output.data(), entries.data(), output.size());
+ return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
+ }
ASSERT(object->status == nvmap::Object::Status::Allocated);
@@ -157,15 +164,21 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
const auto itr = buffer_mappings.find(params.offset);
- ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
+ if (itr == buffer_mappings.end()) {
+ LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset);
+ // Hardware tests shows that unmapping an already unmapped buffer always returns successful
+ // and doesn't fail.
+ return 0;
+ }
auto& system_instance = Core::System::GetInstance();
// Remove this memory region from the rasterizer cache.
- system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
- itr->second.size);
-
auto& gpu = system_instance.GPU();
+ auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
+ ASSERT(cpu_addr);
+ system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
+
params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index a2287cc1b..43651d8a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,6 +11,13 @@
namespace Service::Nvidia::Devices {
+namespace NvErrCodes {
+enum {
+ OperationNotPermitted = -1,
+ InvalidValue = -22,
+};
+}
+
nvmap::nvmap() = default;
nvmap::~nvmap() = default;
@@ -44,7 +51,11 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
+ LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
+ if (!params.size) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
// Create a new nvmap object and obtain a handle to it.
auto object = std::make_shared<Object>();
object->id = next_id++;
@@ -55,8 +66,6 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
u32 handle = next_handle++;
handles[handle] = std::move(object);
- LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
-
params.handle = handle;
std::memcpy(output.data(), &params, sizeof(params));
@@ -66,9 +75,29 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
IocAllocParams params;
std::memcpy(&params, input.data(), sizeof(params));
+ LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
+
+ if (!params.handle) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
+
+ if ((params.align - 1) & params.align) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
+
+ const u32 min_alignment = 0x1000;
+ if (params.align < min_alignment) {
+ params.align = min_alignment;
+ }
auto object = GetObject(params.handle);
- ASSERT(object);
+ if (!object) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
+
+ if (object->status == Object::Status::Allocated) {
+ return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ }
object->flags = params.flags;
object->align = params.align;
@@ -76,8 +105,6 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
object->addr = params.addr;
object->status = Object::Status::Allocated;
- LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
-
std::memcpy(output.data(), &params, sizeof(params));
return 0;
}
@@ -88,8 +115,14 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "called");
+ if (!params.handle) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
+
auto object = GetObject(params.handle);
- ASSERT(object);
+ if (!object) {
+ return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ }
params.id = object->id;
@@ -105,7 +138,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = std::find_if(handles.begin(), handles.end(),
[&](const auto& entry) { return entry.second->id == params.id; });
- ASSERT(itr != handles.end());
+ if (itr == handles.end()) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
+
+ auto& object = itr->second;
+ if (object->status != Object::Status::Allocated) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
itr->second->refcount++;
@@ -125,8 +165,13 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
auto object = GetObject(params.handle);
- ASSERT(object);
- ASSERT(object->status == Object::Status::Allocated);
+ if (!object) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
+
+ if (object->status != Object::Status::Allocated) {
+ return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ }
switch (static_cast<ParamTypes>(params.param)) {
case ParamTypes::Size:
@@ -163,9 +208,12 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
auto itr = handles.find(params.handle);
- ASSERT(itr != handles.end());
-
- ASSERT(itr->second->refcount > 0);
+ if (itr == handles.end()) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
+ if (!itr->second->refcount) {
+ return static_cast<u32>(NvErrCodes::InvalidValue);
+ }
itr->second->refcount--;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index fd98d541d..630ebbfc7 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -31,7 +31,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
buffer_wait_event->Signal();
}
-boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
+std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
// Only consider free buffers. Buffers become free once again after they've been Acquired
// and Released by the compositor, see the NVFlinger::Compose method.
@@ -44,7 +44,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
});
if (itr == queue.end()) {
- return boost::none;
+ return {};
}
itr->status = Buffer::Status::Dequeued;
@@ -70,12 +70,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
itr->crop_rect = crop_rect;
}
-boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
+std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
return buffer.status == Buffer::Status::Queued;
});
if (itr == queue.end())
- return boost::none;
+ return {};
itr->status = Buffer::Status::Acquired;
return *itr;
}
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 50b767732..8cff5eb71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,8 +4,9 @@
#pragma once
+#include <optional>
#include <vector>
-#include <boost/optional.hpp>
+
#include "common/common_funcs.h"
#include "common/math_util.h"
#include "common/swap.h"
@@ -57,9 +58,9 @@ public:
/// Rotate source image 90 degrees clockwise
Rotate90 = 0x04,
/// Rotate source image 180 degrees
- Roate180 = 0x03,
+ Rotate180 = 0x03,
/// Rotate source image 270 degrees clockwise
- Roate270 = 0x07,
+ Rotate270 = 0x07,
};
struct Buffer {
@@ -73,11 +74,11 @@ public:
};
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
- boost::optional<u32> DequeueBuffer(u32 width, u32 height);
+ std::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
- boost::optional<const Buffer&> AcquireBuffer();
+ std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index d47b6f659..214e6d1b3 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -3,7 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <boost/optional.hpp>
+#include <optional>
#include "common/alignment.h"
#include "common/assert.h"
@@ -134,7 +134,7 @@ void NVFlinger::Compose() {
MicroProfileFlip();
- if (buffer == boost::none) {
+ if (!buffer) {
auto& system_instance = Core::System::GetInstance();
// There was no queued buffer to draw, render previous frame
@@ -143,7 +143,7 @@ void NVFlinger::Compose() {
continue;
}
- auto& igbp_buffer = buffer->igbp_buffer;
+ auto& igbp_buffer = buffer->get().igbp_buffer;
// Now send the buffer to the GPU for drawing.
// TODO(Subv): Support more than just disp0. The display device selection is probably based
@@ -152,10 +152,10 @@ void NVFlinger::Compose() {
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
- igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform,
- buffer->crop_rect);
+ igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
+ buffer->get().transform, buffer->get().crop_rect);
- buffer_queue->ReleaseBuffer(buffer->slot);
+ buffer_queue->ReleaseBuffer(buffer->get().slot);
}
}
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 6a9eccfb5..e4fcee9f8 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -14,20 +14,24 @@ public:
explicit PlayReport(const char* name) : ServiceFramework{name} {
// clang-format off
static const FunctionInfo functions[] = {
- {10100, nullptr, "SaveReport"},
- {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"},
+ {10100, nullptr, "SaveReportOld"},
+ {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"},
+ {10102, nullptr, "SaveReport"},
+ {10103, nullptr, "SaveReportWithUser"},
{10200, nullptr, "RequestImmediateTransmission"},
{10300, nullptr, "GetTransmissionStatus"},
{20100, nullptr, "SaveSystemReport"},
- {20200, nullptr, "SetOperationMode"},
{20101, nullptr, "SaveSystemReportWithUser"},
+ {20200, nullptr, "SetOperationMode"},
{30100, nullptr, "ClearStorage"},
+ {30200, nullptr, "ClearStatistics"},
+ {30300, nullptr, "GetStorageUsage"},
+ {30400, nullptr, "GetStatistics"},
+ {30401, nullptr, "GetThroughputHistory"},
+ {30500, nullptr, "GetLastUploadError"},
{40100, nullptr, "IsUserAgreementCheckEnabled"},
{40101, nullptr, "SetUserAgreementCheckEnabled"},
- {90100, nullptr, "GetStorageUsage"},
- {90200, nullptr, "GetStatistics"},
- {90201, nullptr, "GetThroughputHistory"},
- {90300, nullptr, "GetLastUploadError"},
+ {90100, nullptr, "ReadAllReportFiles"},
};
// clang-format on
@@ -35,7 +39,7 @@ public:
}
private:
- void SaveReportWithUser(Kernel::HLERequestContext& ctx) {
+ void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
// TODO(ogniK): Do we want to add play report?
LOG_WARNING(Service_PREPO, "(STUBBED) called");
@@ -46,6 +50,7 @@ private:
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager);
+ std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager);
std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager);
std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager);
std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
new file mode 100644
index 000000000..c2d5fda94
--- /dev/null
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -0,0 +1,71 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::PSM {
+
+constexpr u32 BATTERY_FULLY_CHARGED = 100; // 100% Full
+constexpr u32 BATTERY_CURRENTLY_CHARGING = 1; // Plugged into an official dock
+
+class PSM final : public ServiceFramework<PSM> {
+public:
+ explicit PSM() : ServiceFramework{"psm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
+ {1, &PSM::GetChargerType, "GetChargerType"},
+ {2, nullptr, "EnableBatteryCharging"},
+ {3, nullptr, "DisableBatteryCharging"},
+ {4, nullptr, "IsBatteryChargingEnabled"},
+ {5, nullptr, "AcquireControllerPowerSupply"},
+ {6, nullptr, "ReleaseControllerPowerSupply"},
+ {7, nullptr, "OpenSession"},
+ {8, nullptr, "EnableEnoughPowerChargeEmulation"},
+ {9, nullptr, "DisableEnoughPowerChargeEmulation"},
+ {10, nullptr, "EnableFastBatteryCharging"},
+ {11, nullptr, "DisableFastBatteryCharging"},
+ {12, nullptr, "GetBatteryVoltageState"},
+ {13, nullptr, "GetRawBatteryChargePercentage"},
+ {14, nullptr, "IsEnoughPowerSupplied"},
+ {15, nullptr, "GetBatteryAgePercentage"},
+ {16, nullptr, "GetBatteryChargeInfoEvent"},
+ {17, nullptr, "GetBatteryChargeInfoFields"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+ ~PSM() override = default;
+
+private:
+ void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PSM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(BATTERY_FULLY_CHARGED);
+ }
+
+ void GetChargerType(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PSM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(BATTERY_CURRENTLY_CHARGING);
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& sm) {
+ std::make_shared<PSM>()->InstallAsService(sm);
+}
+
+} // namespace Service::PSM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
new file mode 100644
index 000000000..a286793ae
--- /dev/null
+++ b/src/core/hle/service/ptm/psm.h
@@ -0,0 +1,15 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::PSM {
+
+void InstallInterfaces(SM::ServiceManager& sm);
+
+} // namespace Service::PSM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 62f049660..a4cf45267 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -22,7 +22,7 @@
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/arp/arp.h"
#include "core/hle/service/audio/audio.h"
-#include "core/hle/service/bcat/bcat.h"
+#include "core/hle/service/bcat/module.h"
#include "core/hle/service/bpc/bpc.h"
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/btm/btm.h"
@@ -48,15 +48,17 @@
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nim/nim.h"
+#include "core/hle/service/npns/npns.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/pcie/pcie.h"
-#include "core/hle/service/pctl/pctl.h"
+#include "core/hle/service/pctl/module.h"
#include "core/hle/service/pcv/pcv.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/psc/psc.h"
+#include "core/hle/service/ptm/psm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings.h"
#include "core/hle/service/sm/sm.h"
@@ -197,7 +199,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 +222,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);
@@ -236,6 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
NFP::InstallInterfaces(*sm);
NIFM::InstallInterfaces(*sm);
NIM::InstallInterfaces(*sm);
+ NPNS::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm);
Nvidia::InstallInterfaces(*sm, *nv_flinger);
PCIe::InstallInterfaces(*sm);
@@ -244,6 +247,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
PlayReport::InstallInterfaces(*sm);
PM::InstallInterfaces(*sm);
PSC::InstallInterfaces(*sm);
+ PSM::InstallInterfaces(*sm);
Set::InstallInterfaces(*sm);
Sockets::InstallInterfaces(*sm);
SPL::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/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index e7fb5a419..f0a831d45 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -67,15 +67,15 @@ public:
explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
+ {0, nullptr, "Open"},
+ {1, nullptr, "Close"},
+ {2, nullptr, "Unknown1"},
+ {3, nullptr, "Populate"},
{4, nullptr, "PostBufferAsync"},
- {5, nullptr, "Unknown5"},
- {6, nullptr, "Unknown6"},
- {7, nullptr, "Unknown7"},
- {8, nullptr, "Unknown8"},
+ {5, nullptr, "GetXferReport"},
+ {6, nullptr, "Unknown2"},
+ {7, nullptr, "Unknown3"},
+ {8, nullptr, "Unknown4"},
};
// clang-format on
@@ -89,15 +89,15 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
- {4, nullptr, "Unknown5"},
+ {1, nullptr, "SetInterface"},
+ {2, nullptr, "GetInterface"},
+ {3, nullptr, "GetAlternateInterface"},
+ {4, nullptr, "GetCurrentFrame"},
{5, nullptr, "CtrlXferAsync"},
- {6, nullptr, "Unknown6"},
+ {6, nullptr, "Unknown2"},
{7, nullptr, "GetCtrlXferReport"},
- {8, nullptr, "Unknown7"},
- {9, nullptr, "GetClientEpSession"},
+ {8, nullptr, "ResetDevice"},
+ {9, nullptr, "OpenUsbEp"},
};
// clang-format on
@@ -111,13 +111,14 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindClientProcess"},
- {1, nullptr, "Unknown1"},
- {2, nullptr, "Unknown2"},
- {3, nullptr, "Unknown3"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "Unknown5"},
+ {1, nullptr, "QueryAllInterfaces"},
+ {2, nullptr, "QueryAvailableInterfaces"},
+ {3, nullptr, "QueryAcquiredInterfaces"},
+ {4, nullptr, "CreateInterfaceAvailableEvent"},
+ {5, nullptr, "DestroyInterfaceAvailableEvent"},
{6, nullptr, "GetInterfaceStateChangeEvent"},
- {7, nullptr, "GetClientIfSession"},
+ {7, nullptr, "AcquireUsbIf"},
+ {8, nullptr, "Unknown1"},
};
// clang-format on
@@ -131,11 +132,11 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindNoticeEvent"},
- {1, nullptr, "Unknown1"},
+ {1, nullptr, "UnbindNoticeEvent"},
{2, nullptr, "GetStatus"},
{3, nullptr, "GetNotice"},
- {4, nullptr, "Unknown2"},
- {5, nullptr, "Unknown3"},
+ {4, nullptr, "EnablePowerRequestNotice"},
+ {5, nullptr, "DisablePowerRequestNotice"},
{6, nullptr, "ReplyPowerRequest"},
};
// clang-format on
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index bbc02abcc..d764b2406 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -6,9 +6,10 @@
#include <array>
#include <cstring>
#include <memory>
+#include <optional>
#include <type_traits>
#include <utility>
-#include <boost/optional.hpp>
+
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
@@ -506,9 +507,9 @@ private:
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
const u32 width{request.data.width};
const u32 height{request.data.height};
- boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
+ std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
- if (slot != boost::none) {
+ if (slot) {
// Buffer is available
IGBPDequeueBufferResponseParcel response{*slot};
ctx.WriteBuffer(response.Serialize());
@@ -520,7 +521,7 @@ private:
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
auto buffer_queue = nv_flinger->GetBufferQueue(id);
- boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
+ std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
IGBPDequeueBufferResponseParcel response{*slot};
ctx.WriteBuffer(response.Serialize());
IPC::ResponseBuilder rb{ctx, 2};
@@ -968,6 +969,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 +1040,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"},