summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/hle/ipc_helpers.h32
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp211
-rw-r--r--src/core/hle/kernel/address_arbiter.h3
-rw-r--r--src/core/hle/kernel/client_port.cpp2
-rw-r--r--src/core/hle/kernel/client_session.cpp6
-rw-r--r--src/core/hle/kernel/client_session.h9
-rw-r--r--src/core/hle/kernel/errors.h2
-rw-r--r--src/core/hle/kernel/handle_table.cpp12
-rw-r--r--src/core/hle/kernel/handle_table.h7
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp114
-rw-r--r--src/core/hle/kernel/hle_ipc.h55
-rw-r--r--src/core/hle/kernel/kernel.cpp437
-rw-r--r--src/core/hle/kernel/kernel.h84
-rw-r--r--src/core/hle/kernel/memory/address_space_info.cpp117
-rw-r--r--src/core/hle/kernel/memory/address_space_info.h34
-rw-r--r--src/core/hle/kernel/memory/memory_block.h335
-rw-r--r--src/core/hle/kernel/memory/memory_block_manager.cpp223
-rw-r--r--src/core/hle/kernel/memory/memory_block_manager.h66
-rw-r--r--src/core/hle/kernel/memory/memory_layout.h71
-rw-r--r--src/core/hle/kernel/memory/memory_manager.cpp175
-rw-r--r--src/core/hle/kernel/memory/memory_manager.h96
-rw-r--r--src/core/hle/kernel/memory/memory_types.h18
-rw-r--r--src/core/hle/kernel/memory/page_heap.cpp119
-rw-r--r--src/core/hle/kernel/memory/page_heap.h370
-rw-r--r--src/core/hle/kernel/memory/page_linked_list.h92
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp1174
-rw-r--r--src/core/hle/kernel/memory/page_table.h277
-rw-r--r--src/core/hle/kernel/memory/slab_heap.h163
-rw-r--r--src/core/hle/kernel/memory/system_control.cpp40
-rw-r--r--src/core/hle/kernel/memory/system_control.h13
-rw-r--r--src/core/hle/kernel/mutex.cpp119
-rw-r--r--src/core/hle/kernel/mutex.h4
-rw-r--r--src/core/hle/kernel/physical_core.cpp60
-rw-r--r--src/core/hle/kernel/physical_core.h44
-rw-r--r--src/core/hle/kernel/physical_memory.h2
-rw-r--r--src/core/hle/kernel/process.cpp226
-rw-r--r--src/core/hle/kernel/process.h49
-rw-r--r--src/core/hle/kernel/process_capability.cpp54
-rw-r--r--src/core/hle/kernel/process_capability.h24
-rw-r--r--src/core/hle/kernel/readable_event.cpp16
-rw-r--r--src/core/hle/kernel/resource_limit.cpp48
-rw-r--r--src/core/hle/kernel/resource_limit.h12
-rw-r--r--src/core/hle/kernel/scheduler.cpp570
-rw-r--r--src/core/hle/kernel/scheduler.h125
-rw-r--r--src/core/hle/kernel/server_session.cpp33
-rw-r--r--src/core/hle/kernel/server_session.h15
-rw-r--r--src/core/hle/kernel/shared_memory.cpp151
-rw-r--r--src/core/hle/kernel/shared_memory.h127
-rw-r--r--src/core/hle/kernel/svc.cpp970
-rw-r--r--src/core/hle/kernel/svc.h6
-rw-r--r--src/core/hle/kernel/svc_types.h68
-rw-r--r--src/core/hle/kernel/svc_wrap.h137
-rw-r--r--src/core/hle/kernel/synchronization.cpp136
-rw-r--r--src/core/hle/kernel/synchronization_object.cpp64
-rw-r--r--src/core/hle/kernel/synchronization_object.h18
-rw-r--r--src/core/hle/kernel/thread.cpp428
-rw-r--r--src/core/hle/kernel/thread.h284
-rw-r--r--src/core/hle/kernel/time_manager.cpp32
-rw-r--r--src/core/hle/kernel/time_manager.h4
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp104
-rw-r--r--src/core/hle/kernel/transfer_memory.h55
-rw-r--r--src/core/hle/kernel/vm_manager.cpp1175
-rw-r--r--src/core/hle/kernel/vm_manager.h796
-rw-r--r--src/core/hle/result.h12
-rw-r--r--src/core/hle/service/acc/acc.cpp449
-rw-r--r--src/core/hle/service/acc/acc.h5
-rw-r--r--src/core/hle/service/acc/acc_aa.cpp4
-rw-r--r--src/core/hle/service/acc/acc_su.cpp32
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp18
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp28
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp65
-rw-r--r--src/core/hle/service/am/am.cpp208
-rw-r--r--src/core/hle/service/am/am.h21
-rw-r--r--src/core/hle/service/am/applet_ae.cpp4
-rw-r--r--src/core/hle/service/am/applets/applets.cpp72
-rw-r--r--src/core/hle/service/am/applets/applets.h19
-rw-r--r--src/core/hle/service/am/applets/controller.cpp252
-rw-r--r--src/core/hle/service/am/applets/controller.h135
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp64
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h1
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp19
-rw-r--r--src/core/hle/service/am/spsm.cpp16
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp5
-rw-r--r--src/core/hle/service/apm/apm.cpp1
-rw-r--r--src/core/hle/service/apm/controller.cpp3
-rw-r--r--src/core/hle/service/audio/audctl.cpp2
-rw-r--r--src/core/hle/service/audio/audin_u.cpp70
-rw-r--r--src/core/hle/service/audio/audin_u.h29
-rw-r--r--src/core/hle/service/audio/audout_u.cpp6
-rw-r--r--src/core/hle/service/audio/audren_u.cpp156
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp55
-rw-r--r--src/core/hle/service/bcat/bcat.cpp2
-rw-r--r--src/core/hle/service/bcat/module.cpp7
-rw-r--r--src/core/hle/service/bpc/bpc.cpp20
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp168
-rw-r--r--src/core/hle/service/btm/btm.cpp148
-rw-r--r--src/core/hle/service/caps/caps.cpp2
-rw-r--r--src/core/hle/service/caps/caps.h76
-rw-r--r--src/core/hle/service/caps/caps_a.cpp2
-rw-r--r--src/core/hle/service/caps/caps_a.h2
-rw-r--r--src/core/hle/service/caps/caps_c.cpp18
-rw-r--r--src/core/hle/service/caps/caps_c.h5
-rw-r--r--src/core/hle/service/caps/caps_sc.cpp2
-rw-r--r--src/core/hle/service/caps/caps_sc.h2
-rw-r--r--src/core/hle/service/caps/caps_ss.cpp2
-rw-r--r--src/core/hle/service/caps/caps_ss.h2
-rw-r--r--src/core/hle/service/caps/caps_su.cpp19
-rw-r--r--src/core/hle/service/caps/caps_su.h5
-rw-r--r--src/core/hle/service/caps/caps_u.cpp56
-rw-r--r--src/core/hle/service/caps/caps_u.h4
-rw-r--r--src/core/hle/service/es/es.cpp53
-rw-r--r--src/core/hle/service/eupld/eupld.cpp1
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp103
-rw-r--r--src/core/hle/service/filesystem/filesystem.h6
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp140
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h14
-rw-r--r--src/core/hle/service/friend/friend.cpp8
-rw-r--r--src/core/hle/service/glue/arp.cpp1
-rw-r--r--src/core/hle/service/glue/errors.h8
-rw-r--r--src/core/hle/service/grc/grc.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h4
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp55
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp18
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp845
-rw-r--r--src/core/hle/service/hid/controllers/npad.h223
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp904
-rw-r--r--src/core/hle/service/hid/hid.h59
-rw-r--r--src/core/hle/service/hid/irs.cpp8
-rw-r--r--src/core/hle/service/lbl/lbl.cpp1
-rw-r--r--src/core/hle/service/ldn/ldn.cpp1
-rw-r--r--src/core/hle/service/ldr/ldr.cpp426
-rw-r--r--src/core/hle/service/lm/lm.cpp9
-rw-r--r--src/core/hle/service/lm/manager.cpp3
-rw-r--r--src/core/hle/service/mig/mig.cpp6
-rw-r--r--src/core/hle/service/mii/manager.cpp484
-rw-r--r--src/core/hle/service/mii/manager.h331
-rw-r--r--src/core/hle/service/mii/mii.cpp316
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp420
-rw-r--r--src/core/hle/service/mii/mii_manager.h273
-rw-r--r--src/core/hle/service/mii/raw_data.cpp2261
-rw-r--r--src/core/hle/service/mii/raw_data.h27
-rw-r--r--src/core/hle/service/mii/types.h67
-rw-r--r--src/core/hle/service/mm/mm_u.cpp32
-rw-r--r--src/core/hle/service/ncm/ncm.cpp21
-rw-r--r--src/core/hle/service/nfc/nfc.cpp6
-rw-r--r--src/core/hle/service/nfp/nfp.cpp31
-rw-r--r--src/core/hle/service/nifm/nifm.cpp28
-rw-r--r--src/core/hle/service/nim/nim.cpp158
-rw-r--r--src/core/hle/service/npns/npns.cpp3
-rw-r--r--src/core/hle/service/ns/ns.cpp80
-rw-r--r--src/core/hle/service/ns/ns.h34
-rw-r--r--src/core/hle/service/ns/pl_u.cpp27
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h39
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp327
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h168
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp120
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h113
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp200
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h76
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp294
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h169
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp72
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h32
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp229
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h196
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp64
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h37
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp142
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h75
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp210
-rw-r--r--src/core/hle/service/nvdrv/interface.h10
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h86
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp119
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h39
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.cpp8
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.h4
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp39
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.h85
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp43
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp79
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h43
-rw-r--r--src/core/hle/service/olsc/olsc.cpp69
-rw-r--r--src/core/hle/service/olsc/olsc.h16
-rw-r--r--src/core/hle/service/pcie/pcie.cpp3
-rw-r--r--src/core/hle/service/pctl/module.cpp2
-rw-r--r--src/core/hle/service/pcv/pcv.cpp3
-rw-r--r--src/core/hle/service/pm/pm.cpp35
-rw-r--r--src/core/hle/service/prepo/prepo.cpp25
-rw-r--r--src/core/hle/service/psc/psc.cpp2
-rw-r--r--src/core/hle/service/ptm/psm.cpp22
-rw-r--r--src/core/hle/service/service.cpp15
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/set/set.cpp80
-rw-r--r--src/core/hle/service/set/set.h2
-rw-r--r--src/core/hle/service/set/set_cal.cpp2
-rw-r--r--src/core/hle/service/set/set_sys.cpp14
-rw-r--r--src/core/hle/service/sm/controller.cpp21
-rw-r--r--src/core/hle/service/sm/controller.h6
-rw-r--r--src/core/hle/service/sm/sm.cpp27
-rw-r--r--src/core/hle/service/sm/sm.h12
-rw-r--r--src/core/hle/service/sockets/blocking_worker.h161
-rw-r--r--src/core/hle/service/sockets/bsd.cpp811
-rw-r--r--src/core/hle/service/sockets/bsd.h150
-rw-r--r--src/core/hle/service/sockets/nsd.cpp6
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp25
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h2
-rw-r--r--src/core/hle/service/sockets/sockets.cpp6
-rw-r--r--src/core/hle/service/sockets/sockets.h85
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp165
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h48
-rw-r--r--src/core/hle/service/spl/module.cpp2
-rw-r--r--src/core/hle/service/spl/spl.cpp39
-rw-r--r--src/core/hle/service/time/interface.cpp2
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h2
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/steady_clock_core.h1
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h2
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp2
-rw-r--r--src/core/hle/service/time/system_clock_core.h2
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/time.cpp105
-rw-r--r--src/core/hle/service/time/time.h10
-rw-r--r--src/core/hle/service/time/time_manager.cpp352
-rw-r--r--src/core/hle/service/time/time_manager.h85
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp8
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp27
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h4
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp39
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp4
-rw-r--r--src/core/hle/service/usb/usb.cpp31
-rw-r--r--src/core/hle/service/vi/vi.cpp127
-rw-r--r--src/core/hle/service/vi/vi_u.cpp1
-rw-r--r--src/core/hle/service/wlan/wlan.cpp102
246 files changed, 18188 insertions, 7939 deletions
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 0dc6a4a43..d57776ce9 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -12,7 +12,6 @@
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
-#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -38,10 +37,11 @@ public:
explicit RequestHelperBase(Kernel::HLERequestContext& context)
: context(&context), cmdbuf(context.CommandBuffer()) {}
- void Skip(unsigned size_in_words, bool set_to_null) {
- if (set_to_null)
+ void Skip(u32 size_in_words, bool set_to_null) {
+ if (set_to_null) {
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
- index += size_in_words;
+ }
+ index += static_cast<ptrdiff_t>(size_in_words);
}
/**
@@ -49,15 +49,15 @@ public:
*/
void AlignWithPadding() {
if (index & 3) {
- Skip(4 - (index & 3), true);
+ Skip(static_cast<u32>(4 - (index & 3)), true);
}
}
- unsigned GetCurrentOffset() const {
- return static_cast<unsigned>(index);
+ u32 GetCurrentOffset() const {
+ return static_cast<u32>(index);
}
- void SetCurrentOffset(unsigned offset) {
+ void SetCurrentOffset(u32 offset) {
index = static_cast<ptrdiff_t>(offset);
}
};
@@ -72,14 +72,12 @@ public:
AlwaysMoveHandles = 1,
};
- explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
-
explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
-
: RequestHelperBase(context), normal_params_size(normal_params_size),
- num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
+ num_handles_to_copy(num_handles_to_copy),
+ num_objects_to_move(num_objects_to_move), kernel{context.kernel} {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
@@ -89,7 +87,7 @@ public:
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
- u32 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
+ u64 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
u32 num_handles_to_move{};
u32 num_domain_objects{};
@@ -105,7 +103,7 @@ public:
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
}
- header.data_size.Assign(raw_data_size);
+ header.data_size.Assign(static_cast<u32>(raw_data_size));
if (num_handles_to_copy || num_handles_to_move) {
header.enable_handle_descriptor.Assign(1);
}
@@ -139,7 +137,6 @@ public:
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
- auto& kernel = Core::System::GetInstance().Kernel();
auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
context->AddMoveObject(std::move(client));
iface->ClientConnected(std::move(server));
@@ -213,6 +210,7 @@ private:
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
+ Kernel::KernelCore& kernel;
};
/// Push ///
@@ -229,6 +227,8 @@ inline void ResponseBuilder::Push(u32 value) {
template <typename T>
void ResponseBuilder::PushRaw(const T& value) {
+ static_assert(std::is_trivially_copyable_v<T>,
+ "It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
@@ -384,6 +384,8 @@ inline s32 RequestParser::Pop() {
template <typename T>
void RequestParser::PopRaw(T& value) {
+ static_assert(std::is_trivially_copyable_v<T>,
+ "It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 8475b698c..b882eaa0f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -7,11 +7,15 @@
#include "common/assert.h"
#include "common/common_types.h"
+#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -29,12 +33,10 @@ void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& wai
// Signal the waiting threads.
for (std::size_t i = 0; i < last; i++) {
- ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
- waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
+ waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
RemoveThread(waiting_threads[i]);
- waiting_threads[i]->SetArbiterWaitAddress(0);
+ waiting_threads[i]->WaitForArbitration(false);
waiting_threads[i]->ResumeFromWait();
- system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
}
}
@@ -56,6 +58,7 @@ ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 v
}
ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
+ SchedulerLock lock(system.Kernel());
const std::vector<std::shared_ptr<Thread>> waiting_threads =
GetThreadsWaitingOnAddress(address);
WakeThreads(waiting_threads, num_to_wake);
@@ -64,6 +67,7 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) {
+ SchedulerLock lock(system.Kernel());
auto& memory = system.Memory();
// Ensure that we can write to the address.
@@ -71,16 +75,24 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
return ERR_INVALID_ADDRESS_STATE;
}
- if (static_cast<s32>(memory.Read32(address)) != value) {
- return ERR_INVALID_STATE;
- }
+ const std::size_t current_core = system.CurrentCoreIndex();
+ auto& monitor = system.Monitor();
+ u32 current_value;
+ do {
+ current_value = monitor.ExclusiveRead32(current_core, address);
+
+ if (current_value != static_cast<u32>(value)) {
+ return ERR_INVALID_STATE;
+ }
+ current_value++;
+ } while (!monitor.ExclusiveWrite32(current_core, address, current_value));
- memory.Write32(address, static_cast<u32>(value + 1));
return SignalToAddressOnly(address, num_to_wake);
}
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) {
+ SchedulerLock lock(system.Kernel());
auto& memory = system.Memory();
// Ensure that we can write to the address.
@@ -92,29 +104,33 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
const std::vector<std::shared_ptr<Thread>> waiting_threads =
GetThreadsWaitingOnAddress(address);
- // Determine the modified value depending on the waiting count.
+ const std::size_t current_core = system.CurrentCoreIndex();
+ auto& monitor = system.Monitor();
s32 updated_value;
- if (num_to_wake <= 0) {
- if (waiting_threads.empty()) {
- updated_value = value + 1;
- } else {
- updated_value = value - 1;
+ do {
+ updated_value = monitor.ExclusiveRead32(current_core, address);
+
+ if (updated_value != value) {
+ return ERR_INVALID_STATE;
}
- } else {
- if (waiting_threads.empty()) {
- updated_value = value + 1;
- } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
- updated_value = value - 1;
+ // Determine the modified value depending on the waiting count.
+ if (num_to_wake <= 0) {
+ if (waiting_threads.empty()) {
+ updated_value = value + 1;
+ } else {
+ updated_value = value - 1;
+ }
} else {
- updated_value = value;
+ if (waiting_threads.empty()) {
+ updated_value = value + 1;
+ } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
+ updated_value = value - 1;
+ } else {
+ updated_value = value;
+ }
}
- }
+ } while (!monitor.ExclusiveWrite32(current_core, address, updated_value));
- if (static_cast<s32>(memory.Read32(address)) != value) {
- return ERR_INVALID_STATE;
- }
-
- memory.Write32(address, static_cast<u32>(updated_value));
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
@@ -136,60 +152,127 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
bool should_decrement) {
auto& memory = system.Memory();
+ auto& kernel = system.Kernel();
+ Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
- // Ensure that we can read the address.
- if (!memory.IsValidVirtualAddress(address)) {
- return ERR_INVALID_ADDRESS_STATE;
- }
+ Handle event_handle = InvalidHandle;
+ {
+ SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
+
+ if (current_thread->IsPendingTermination()) {
+ lock.CancelSleep();
+ return ERR_THREAD_TERMINATING;
+ }
+
+ // Ensure that we can read the address.
+ if (!memory.IsValidVirtualAddress(address)) {
+ lock.CancelSleep();
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ s32 current_value = static_cast<s32>(memory.Read32(address));
+ if (current_value >= value) {
+ lock.CancelSleep();
+ return ERR_INVALID_STATE;
+ }
+
+ current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
+
+ s32 decrement_value;
+
+ const std::size_t current_core = system.CurrentCoreIndex();
+ auto& monitor = system.Monitor();
+ do {
+ current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
+ if (should_decrement) {
+ decrement_value = current_value - 1;
+ } else {
+ decrement_value = current_value;
+ }
+ } while (
+ !monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value)));
+
+ // Short-circuit without rescheduling, if timeout is zero.
+ if (timeout == 0) {
+ lock.CancelSleep();
+ return RESULT_TIMEOUT;
+ }
- const s32 cur_value = static_cast<s32>(memory.Read32(address));
- if (cur_value >= value) {
- return ERR_INVALID_STATE;
+ current_thread->SetArbiterWaitAddress(address);
+ InsertThread(SharedFrom(current_thread));
+ current_thread->SetStatus(ThreadStatus::WaitArb);
+ current_thread->WaitForArbitration(true);
}
- if (should_decrement) {
- memory.Write32(address, static_cast<u32>(cur_value - 1));
+ if (event_handle != InvalidHandle) {
+ auto& time_manager = kernel.TimeManager();
+ time_manager.UnscheduleTimeEvent(event_handle);
}
- // Short-circuit without rescheduling, if timeout is zero.
- if (timeout == 0) {
- return RESULT_TIMEOUT;
+ {
+ SchedulerLock lock(kernel);
+ if (current_thread->IsWaitingForArbitration()) {
+ RemoveThread(SharedFrom(current_thread));
+ current_thread->WaitForArbitration(false);
+ }
}
- return WaitForAddressImpl(address, timeout);
+ return current_thread->GetSignalingResult();
}
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
auto& memory = system.Memory();
+ auto& kernel = system.Kernel();
+ Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
- // Ensure that we can read the address.
- if (!memory.IsValidVirtualAddress(address)) {
- return ERR_INVALID_ADDRESS_STATE;
- }
+ Handle event_handle = InvalidHandle;
+ {
+ SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
+
+ if (current_thread->IsPendingTermination()) {
+ lock.CancelSleep();
+ return ERR_THREAD_TERMINATING;
+ }
+
+ // Ensure that we can read the address.
+ if (!memory.IsValidVirtualAddress(address)) {
+ lock.CancelSleep();
+ return ERR_INVALID_ADDRESS_STATE;
+ }
- // Only wait for the address if equal.
- if (static_cast<s32>(memory.Read32(address)) != value) {
- return ERR_INVALID_STATE;
+ s32 current_value = static_cast<s32>(memory.Read32(address));
+ if (current_value != value) {
+ lock.CancelSleep();
+ return ERR_INVALID_STATE;
+ }
+
+ // Short-circuit without rescheduling, if timeout is zero.
+ if (timeout == 0) {
+ lock.CancelSleep();
+ return RESULT_TIMEOUT;
+ }
+
+ current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
+ current_thread->SetArbiterWaitAddress(address);
+ InsertThread(SharedFrom(current_thread));
+ current_thread->SetStatus(ThreadStatus::WaitArb);
+ current_thread->WaitForArbitration(true);
}
- // Short-circuit without rescheduling if timeout is zero.
- if (timeout == 0) {
- return RESULT_TIMEOUT;
+ if (event_handle != InvalidHandle) {
+ auto& time_manager = kernel.TimeManager();
+ time_manager.UnscheduleTimeEvent(event_handle);
}
- return WaitForAddressImpl(address, timeout);
-}
+ {
+ SchedulerLock lock(kernel);
+ if (current_thread->IsWaitingForArbitration()) {
+ RemoveThread(SharedFrom(current_thread));
+ current_thread->WaitForArbitration(false);
+ }
+ }
-ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
- Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
- current_thread->SetArbiterWaitAddress(address);
- InsertThread(SharedFrom(current_thread));
- current_thread->SetStatus(ThreadStatus::WaitArb);
- current_thread->InvalidateWakeupCallback();
- current_thread->WakeAfterDelay(timeout);
-
- system.PrepareReschedule(current_thread->GetProcessorID());
- return RESULT_TIMEOUT;
+ return current_thread->GetSignalingResult();
}
void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) {
@@ -221,9 +304,9 @@ void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(),
[&thread](const auto& entry) { return thread == entry; });
- ASSERT(iter != thread_list.cend());
-
- thread_list.erase(iter);
+ if (iter != thread_list.cend()) {
+ thread_list.erase(iter);
+ }
}
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index f958eee5a..0b05d533c 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -73,9 +73,6 @@ private:
/// Waits on an address if the value passed is equal to the argument value.
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
- // Waits on the given address with a timeout in nanoseconds
- ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
-
/// Wake up num_to_wake (or all) threads in a vector.
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 5498fd313..8aff2227a 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
}
// Wake the threads waiting on the ServerPort
- server_port->WakeupAllWaitingThreads();
+ server_port->Signal();
return MakeResult(std::move(client));
}
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 6d66276bc..be9eba519 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -47,14 +47,16 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
return MakeResult(std::move(client_session));
}
-ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) {
+ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
+ Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
// Keep ServerSession alive until we're done working with it.
if (!parent->Server()) {
return ERR_SESSION_CLOSED_BY_REMOTE;
}
// Signal the server session that new data is available
- return parent->Server()->HandleSyncRequest(std::move(thread), memory);
+ return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index d15b09554..e5e0690c2 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -12,10 +12,14 @@
union ResultCode;
-namespace Memory {
+namespace Core::Memory {
class Memory;
}
+namespace Core::Timing {
+class CoreTiming;
+}
+
namespace Kernel {
class KernelCore;
@@ -42,7 +46,8 @@ public:
return HANDLE_TYPE;
}
- ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
+ ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
bool ShouldWait(const Thread* thread) const override;
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 8097b3863..d4e5d88cf 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -12,8 +12,10 @@ namespace Kernel {
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
+constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59};
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
+constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103};
constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index e441a27fc..3e745c18b 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -8,7 +8,9 @@
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -22,7 +24,7 @@ constexpr u16 GetGeneration(Handle handle) {
}
} // Anonymous namespace
-HandleTable::HandleTable() {
+HandleTable::HandleTable(KernelCore& kernel) : kernel{kernel} {
Clear();
}
@@ -30,6 +32,7 @@ HandleTable::~HandleTable() = default;
ResultCode HandleTable::SetSize(s32 handle_table_size) {
if (static_cast<u32>(handle_table_size) > MAX_COUNT) {
+ LOG_ERROR(Kernel, "Handle table size {} is greater than {}", handle_table_size, MAX_COUNT);
return ERR_OUT_OF_MEMORY;
}
@@ -80,6 +83,7 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
ResultCode HandleTable::Close(Handle handle) {
if (!IsValid(handle)) {
+ LOG_ERROR(Kernel, "Handle is not valid! handle={:08X}", handle);
return ERR_INVALID_HANDLE;
}
@@ -101,9 +105,9 @@ bool HandleTable::IsValid(Handle handle) const {
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
- return SharedFrom(GetCurrentThread());
+ return SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
} else if (handle == CurrentProcess) {
- return SharedFrom(Core::System::GetInstance().CurrentProcess());
+ return SharedFrom(kernel.CurrentProcess());
}
if (!IsValid(handle)) {
@@ -114,7 +118,7 @@ std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
void HandleTable::Clear() {
for (u16 i = 0; i < table_size; ++i) {
- generations[i] = i + 1;
+ generations[i] = static_cast<u16>(i + 1);
objects[i] = nullptr;
}
next_free_slot = 0;
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 8029660ed..c9dab8cdd 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -14,6 +14,8 @@
namespace Kernel {
+class KernelCore;
+
enum KernelHandle : Handle {
InvalidHandle = 0,
CurrentThread = 0xFFFF8000,
@@ -48,7 +50,7 @@ public:
/// This is the maximum limit of handles allowed per process in Horizon
static constexpr std::size_t MAX_COUNT = 1024;
- HandleTable();
+ explicit HandleTable(KernelCore& kernel);
~HandleTable();
/**
@@ -134,6 +136,9 @@ private:
/// Head of the free slots linked list.
u16 next_free_slot = 0;
+
+ /// Underlying kernel instance that this handle table operates under.
+ KernelCore& kernel;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index c558a2f33..81f85643b 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -13,16 +13,18 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
@@ -47,31 +49,33 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
const std::string& reason, u64 timeout, WakeupCallback&& callback,
std::shared_ptr<WritableEvent> writable_event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
- thread->SetWakeupCallback(
- [context = *this, callback](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
- std::shared_ptr<SynchronizationObject> object,
- std::size_t index) mutable -> bool {
- ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent);
- callback(thread, context, reason);
- context.WriteToOutgoingCommandBuffer(*thread);
- return true;
- });
-
- auto& kernel = Core::System::GetInstance().Kernel();
+
if (!writable_event) {
// Create event if not provided
const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
writable_event = pair.writable;
}
- const auto readable_event{writable_event->GetReadableEvent()};
- writable_event->Clear();
- thread->SetStatus(ThreadStatus::WaitHLEEvent);
- thread->SetSynchronizationObjects({readable_event});
- readable_event->AddWaitingThread(thread);
-
- if (timeout > 0) {
- thread->WakeAfterDelay(timeout);
+ {
+ Handle event_handle = InvalidHandle;
+ SchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout);
+ thread->SetHLECallback(
+ [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool {
+ ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT
+ ? ThreadWakeupReason::Timeout
+ : ThreadWakeupReason::Signal;
+ callback(thread, context, reason);
+ context.WriteToOutgoingCommandBuffer(*thread);
+ return true;
+ });
+ const auto readable_event{writable_event->GetReadableEvent()};
+ writable_event->Clear();
+ thread->SetHLESyncObject(readable_event.get());
+ thread->SetStatus(ThreadStatus::WaitHLEEvent);
+ thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
+ readable_event->AddWaitingThread(thread);
+ lock.Release();
+ thread->SetHLETimeEvent(event_handle);
}
is_thread_waiting = true;
@@ -79,9 +83,11 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
return writable_event;
}
-HLERequestContext::HLERequestContext(std::shared_ptr<Kernel::ServerSession> server_session,
+HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
+ std::shared_ptr<ServerSession> server_session,
std::shared_ptr<Thread> thread)
- : server_session(std::move(server_session)), thread(std::move(thread)) {
+ : server_session(std::move(server_session)),
+ thread(std::move(thread)), kernel{kernel}, memory{memory} {
cmd_buf[0] = 0;
}
@@ -216,7 +222,6 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
- auto& memory = Core::System::GetInstance().Memory();
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
memory.ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
@@ -282,20 +287,21 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
return RESULT_SUCCESS;
}
-std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
- std::vector<u8> buffer;
+std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
+ std::vector<u8> buffer{};
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
- auto& memory = Core::System::GetInstance().Memory();
if (is_buffer_a) {
- ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
- "BufferDescriptorA invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorA().size() > buffer_index, { return buffer; },
+ "BufferDescriptorA invalid buffer_index {}", buffer_index);
buffer.resize(BufferDescriptorA()[buffer_index].Size());
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
} else {
- ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
- "BufferDescriptorX invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorX().size() > buffer_index, { return buffer; },
+ "BufferDescriptorX invalid buffer_index {}", buffer_index);
buffer.resize(BufferDescriptorX()[buffer_index].Size());
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
}
@@ -304,7 +310,7 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
}
std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
- int buffer_index) const {
+ std::size_t buffer_index) const {
if (size == 0) {
LOG_WARNING(Core, "skip empty buffer write");
return 0;
@@ -319,54 +325,54 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
size = buffer_size; // TODO(bunnei): This needs to be HW tested
}
- auto& memory = Core::System::GetInstance().Memory();
if (is_buffer_b) {
- ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
- "BufferDescriptorB invalid buffer_index {}", buffer_index);
- ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size,
- "BufferDescriptorB buffer_index {} is not large enough", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorB().size() > buffer_index &&
+ BufferDescriptorB()[buffer_index].Size() >= size,
+ { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
} else {
- ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
- "BufferDescriptorC invalid buffer_index {}", buffer_index);
- ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size,
- "BufferDescriptorC buffer_index {} is not large enough", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorC().size() > buffer_index &&
+ BufferDescriptorC()[buffer_index].Size() >= size,
+ { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
}
return size;
}
-std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
+std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
- ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
- "BufferDescriptorA invalid buffer_index {}", buffer_index);
- ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0,
- "BufferDescriptorA buffer_index {} is empty", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorA().size() > buffer_index, { return 0; },
+ "BufferDescriptorA invalid buffer_index {}", buffer_index);
return BufferDescriptorA()[buffer_index].Size();
} else {
- ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
- "BufferDescriptorX invalid buffer_index {}", buffer_index);
- ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0,
- "BufferDescriptorX buffer_index {} is empty", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorX().size() > buffer_index, { return 0; },
+ "BufferDescriptorX invalid buffer_index {}", buffer_index);
return BufferDescriptorX()[buffer_index].Size();
}
}
-std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const {
+std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) const {
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
if (is_buffer_b) {
- ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
- "BufferDescriptorB invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorB().size() > buffer_index, { return 0; },
+ "BufferDescriptorB invalid buffer_index {}", buffer_index);
return BufferDescriptorB()[buffer_index].Size();
} else {
- ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
- "BufferDescriptorC invalid buffer_index {}", buffer_index);
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorC().size() > buffer_index, { return 0; },
+ "BufferDescriptorC invalid buffer_index {}", buffer_index);
return BufferDescriptorC()[buffer_index].Size();
}
+ return 0;
}
std::string HLERequestContext::Description() const {
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 050ad8fd7..c31a65476 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -13,12 +13,21 @@
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
+#include "common/concepts.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/object.h"
union ResultCode;
+namespace Core::Memory {
+class Memory;
+}
+
+namespace IPC {
+class ResponseBuilder;
+}
+
namespace Service {
class ServiceFrameworkBase;
}
@@ -28,6 +37,7 @@ namespace Kernel {
class Domain;
class HandleTable;
class HLERequestContext;
+class KernelCore;
class Process;
class ServerSession;
class Thread;
@@ -98,7 +108,8 @@ protected:
*/
class HLERequestContext {
public:
- explicit HLERequestContext(std::shared_ptr<ServerSession> session,
+ explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
+ std::shared_ptr<ServerSession> session,
std::shared_ptr<Thread> thread);
~HLERequestContext();
@@ -179,36 +190,39 @@ public:
}
/// Helper function to read a buffer using the appropriate buffer descriptor
- std::vector<u8> ReadBuffer(int buffer_index = 0) const;
+ std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
- std::size_t WriteBuffer(const void* buffer, std::size_t size, int buffer_index = 0) const;
+ std::size_t WriteBuffer(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
/* Helper function to write a buffer using the appropriate buffer descriptor
*
- * @tparam ContiguousContainer an arbitrary container that satisfies the
- * ContiguousContainer concept in the C++ standard library.
+ * @tparam T an arbitrary container that satisfies the
+ * ContiguousContainer concept in the C++ standard library or a trivially copyable type.
*
- * @param container The container to write the data of into a buffer.
+ * @param data The container/data to write into a buffer.
* @param buffer_index The buffer in particular to write to.
*/
- template <typename ContiguousContainer,
- typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>>
- std::size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const {
- using ContiguousType = typename ContiguousContainer::value_type;
-
- static_assert(std::is_trivially_copyable_v<ContiguousType>,
- "Container to WriteBuffer must contain trivially copyable objects");
-
- return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType),
- buffer_index);
+ template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
+ std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
+ if constexpr (Common::IsSTLContainer<T>) {
+ using ContiguousType = typename T::value_type;
+ static_assert(std::is_trivially_copyable_v<ContiguousType>,
+ "Container to WriteBuffer must contain trivially copyable objects");
+ return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType),
+ buffer_index);
+ } else {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
+ return WriteBuffer(&data, sizeof(T), buffer_index);
+ }
}
/// Helper function to get the size of the input buffer
- std::size_t GetReadBufferSize(int buffer_index = 0) const;
+ std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to get the size of the output buffer
- std::size_t GetWriteBufferSize(int buffer_index = 0) const;
+ std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
template <typename T>
std::shared_ptr<T> GetCopyObject(std::size_t index) {
@@ -277,6 +291,8 @@ public:
}
private:
+ friend class IPC::ResponseBuilder;
+
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
@@ -303,6 +319,9 @@ private:
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
bool is_thread_waiting{};
+
+ KernelCore& kernel;
+ Core::Memory::Memory& memory;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 014d647cf..bb3e312a7 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,31 +2,40 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
#include <atomic>
#include <bitset>
#include <functional>
#include <memory>
-#include <mutex>
#include <thread>
#include <unordered_map>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
+#include "common/microprofile.h"
+#include "common/thread.h"
#include "core/arm/arm_interface.h"
+#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
+#include "core/cpu_manager.h"
+#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory/memory_layout.h"
+#include "core/hle/kernel/memory/memory_manager.h"
+#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/synchronization.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
@@ -34,84 +43,29 @@
#include "core/hle/result.h"
#include "core/memory.h"
-namespace Kernel {
-
-/**
- * Callback that will wake up the thread it was scheduled for
- * @param thread_handle The handle of the thread that's been awoken
- * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
- */
-static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
- const auto proper_handle = static_cast<Handle>(thread_handle);
- const auto& system = Core::System::GetInstance();
-
- // Lock the global kernel mutex when we enter the kernel HLE.
- std::lock_guard lock{HLE::g_hle_lock};
-
- std::shared_ptr<Thread> thread =
- system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
- if (thread == nullptr) {
- LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
- return;
- }
-
- bool resume = true;
-
- if (thread->GetStatus() == ThreadStatus::WaitSynch ||
- thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
- // Remove the thread from each of its waiting objects' waitlists
- for (const auto& object : thread->GetSynchronizationObjects()) {
- object->RemoveWaitingThread(thread);
- }
- thread->ClearSynchronizationObjects();
-
- // Invoke the wakeup callback before clearing the wait objects
- if (thread->HasWakeupCallback()) {
- resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
- }
- } else if (thread->GetStatus() == ThreadStatus::WaitMutex ||
- thread->GetStatus() == ThreadStatus::WaitCondVar) {
- thread->SetMutexWaitAddress(0);
- thread->SetWaitHandle(0);
- if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
- thread->GetOwnerProcess()->RemoveConditionVariableThread(thread);
- thread->SetCondVarWaitAddress(0);
- }
-
- auto* const lock_owner = thread->GetLockOwner();
- // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
- // and don't have a lock owner unless SignalProcessWideKey was called first and the thread
- // wasn't awakened due to the mutex already being acquired.
- if (lock_owner != nullptr) {
- lock_owner->RemoveMutexWaiter(thread);
- }
- }
-
- if (thread->GetStatus() == ThreadStatus::WaitArb) {
- auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter();
- address_arbiter.HandleWakeupThread(thread);
- }
+MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
- if (resume) {
- if (thread->GetStatus() == ThreadStatus::WaitCondVar ||
- thread->GetStatus() == ThreadStatus::WaitArb) {
- thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
- }
- thread->ResumeFromWait();
- }
-}
+namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system, KernelCore& kernel)
- : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {}
+ : global_scheduler{kernel}, synchronization{system}, time_manager{system},
+ global_handle_table{kernel}, system{system} {}
+
+ void SetMulticore(bool is_multicore) {
+ this->is_multicore = is_multicore;
+ }
void Initialize(KernelCore& kernel) {
Shutdown();
+ RegisterHostThread();
InitializePhysicalCores();
InitializeSystemResourceLimit(kernel);
- InitializeThreads();
- InitializePreemption();
+ InitializeMemoryLayout();
+ InitializePreemption(kernel);
+ InitializeSchedulers();
+ InitializeSuspendThreads();
}
void Shutdown() {
@@ -120,13 +74,24 @@ struct KernelCore::Impl {
next_user_process_id = Process::ProcessIDMin;
next_thread_id = 1;
+ for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ if (suspend_threads[i]) {
+ suspend_threads[i].reset();
+ }
+ }
+
+ for (std::size_t i = 0; i < cores.size(); i++) {
+ cores[i].Shutdown();
+ schedulers[i].reset();
+ }
+ cores.clear();
+
process_list.clear();
current_process = nullptr;
system_resource_limit = nullptr;
global_handle_table.Clear();
- thread_wakeup_event_type = nullptr;
preemption_event = nullptr;
global_scheduler.Shutdown();
@@ -139,13 +104,25 @@ struct KernelCore::Impl {
cores.clear();
exclusive_monitor.reset();
+
+ num_host_threads = 0;
+ std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(),
+ std::thread::id{});
+ std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
}
void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- cores.emplace_back(system, i, *exclusive_monitor);
+ schedulers[i] = std::make_unique<Kernel::Scheduler>(system, i);
+ cores.emplace_back(system, i, *schedulers[i], interrupts[i]);
+ }
+ }
+
+ void InitializeSchedulers() {
+ for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ cores[i].Scheduler().Initialize();
}
}
@@ -154,71 +131,105 @@ struct KernelCore::Impl {
system_resource_limit = ResourceLimit::Create(kernel);
// If setting the default system values fails, then something seriously wrong has occurred.
- ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x100000000)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
- }
- void InitializeThreads() {
- thread_wakeup_event_type =
- Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback);
+ if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) ||
+ !system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) {
+ UNREACHABLE();
+ }
}
- void InitializePreemption() {
- preemption_event =
- Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) {
- global_scheduler.PreemptThreads();
- s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
+ void InitializePreemption(KernelCore& kernel) {
+ preemption_event = Core::Timing::CreateEvent(
+ "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) {
+ {
+ SchedulerLock lock(kernel);
+ global_scheduler.PreemptThreads();
+ }
+ const auto time_interval = std::chrono::nanoseconds{
+ Core::Timing::msToCycles(std::chrono::milliseconds(10))};
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
});
- s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
+ const auto time_interval =
+ std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
}
+ void InitializeSuspendThreads() {
+ for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ std::string name = "Suspend Thread Id:" + std::to_string(i);
+ std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
+ void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
+ const auto type =
+ static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
+ auto thread_res =
+ Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
+ nullptr, std::move(init_func), init_func_parameter);
+
+ suspend_threads[i] = std::move(thread_res).Unwrap();
+ }
+ }
+
void MakeCurrentProcess(Process* process) {
current_process = process;
-
if (process == nullptr) {
return;
}
-
- for (auto& core : cores) {
- core.SetIs64Bit(process->Is64BitProcess());
+ const u32 core_id = GetCurrentHostThreadID();
+ if (core_id < Core::Hardware::NUM_CPU_CORES) {
+ system.Memory().SetCurrentPageTable(*process, core_id);
}
-
- system.Memory().SetCurrentPageTable(*process);
}
void RegisterCoreThread(std::size_t core_id) {
- std::unique_lock lock{register_thread_mutex};
const std::thread::id this_id = std::this_thread::get_id();
- const auto it = host_thread_ids.find(this_id);
+ if (!is_multicore) {
+ single_core_thread_id = this_id;
+ }
+ const auto end =
+ register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
+ const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
- ASSERT(it == host_thread_ids.end());
- ASSERT(!registered_core_threads[core_id]);
- host_thread_ids[this_id] = static_cast<u32>(core_id);
- registered_core_threads.set(core_id);
+ ASSERT(it == end);
+ InsertHostThread(static_cast<u32>(core_id));
}
void RegisterHostThread() {
- std::unique_lock lock{register_thread_mutex};
const std::thread::id this_id = std::this_thread::get_id();
- const auto it = host_thread_ids.find(this_id);
- ASSERT(it == host_thread_ids.end());
- host_thread_ids[this_id] = registered_thread_ids++;
+ const auto end =
+ register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
+ const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
+ if (it == end) {
+ InsertHostThread(registered_thread_ids++);
+ }
+ }
+
+ void InsertHostThread(u32 value) {
+ const size_t index = num_host_threads++;
+ ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
+ register_host_thread_values[index] = value;
+ register_host_thread_keys[index] = std::this_thread::get_id();
}
- u32 GetCurrentHostThreadID() const {
+ [[nodiscard]] u32 GetCurrentHostThreadID() const {
const std::thread::id this_id = std::this_thread::get_id();
- const auto it = host_thread_ids.find(this_id);
- if (it == host_thread_ids.end()) {
+ if (!is_multicore && single_core_thread_id == this_id) {
+ return static_cast<u32>(system.GetCpuManager().CurrentCore());
+ }
+ const auto end =
+ register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
+ const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
+ if (it == end) {
return Core::INVALID_HOST_THREAD_ID;
}
- return it->second;
+ return register_host_thread_values[static_cast<size_t>(
+ std::distance(register_host_thread_keys.begin(), it))];
}
Core::EmuThreadHandle GetCurrentEmuThreadID() const {
@@ -229,7 +240,7 @@ struct KernelCore::Impl {
}
const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler();
const Kernel::Thread* current = sched.GetCurrentThread();
- if (current != nullptr) {
+ if (current != nullptr && !current->IsPhantomMode()) {
result.guest_handle = current->GetGlobalHandle();
} else {
result.guest_handle = InvalidHandle;
@@ -237,6 +248,57 @@ struct KernelCore::Impl {
return result;
}
+ void InitializeMemoryLayout() {
+ // Initialize memory layout
+ constexpr Memory::MemoryLayout layout{Memory::MemoryLayout::GetDefaultLayout()};
+ constexpr std::size_t hid_size{0x40000};
+ constexpr std::size_t font_size{0x1100000};
+ constexpr std::size_t irs_size{0x8000};
+ constexpr std::size_t time_size{0x1000};
+ constexpr PAddr hid_addr{layout.System().StartAddress()};
+ constexpr PAddr font_pa{layout.System().StartAddress() + hid_size};
+ constexpr PAddr irs_addr{layout.System().StartAddress() + hid_size + font_size};
+ constexpr PAddr time_addr{layout.System().StartAddress() + hid_size + font_size + irs_size};
+
+ // Initialize memory manager
+ memory_manager = std::make_unique<Memory::MemoryManager>();
+ memory_manager->InitializeManager(Memory::MemoryManager::Pool::Application,
+ layout.Application().StartAddress(),
+ layout.Application().EndAddress());
+ memory_manager->InitializeManager(Memory::MemoryManager::Pool::Applet,
+ layout.Applet().StartAddress(),
+ layout.Applet().EndAddress());
+ memory_manager->InitializeManager(Memory::MemoryManager::Pool::System,
+ layout.System().StartAddress(),
+ layout.System().EndAddress());
+
+ hid_shared_mem = Kernel::SharedMemory::Create(
+ system.Kernel(), system.DeviceMemory(), nullptr,
+ {hid_addr, hid_size / Memory::PageSize}, Memory::MemoryPermission::None,
+ Memory::MemoryPermission::Read, hid_addr, hid_size, "HID:SharedMemory");
+ font_shared_mem = Kernel::SharedMemory::Create(
+ system.Kernel(), system.DeviceMemory(), nullptr,
+ {font_pa, font_size / Memory::PageSize}, Memory::MemoryPermission::None,
+ Memory::MemoryPermission::Read, font_pa, font_size, "Font:SharedMemory");
+ irs_shared_mem = Kernel::SharedMemory::Create(
+ system.Kernel(), system.DeviceMemory(), nullptr,
+ {irs_addr, irs_size / Memory::PageSize}, Memory::MemoryPermission::None,
+ Memory::MemoryPermission::Read, irs_addr, irs_size, "IRS:SharedMemory");
+ time_shared_mem = Kernel::SharedMemory::Create(
+ system.Kernel(), system.DeviceMemory(), nullptr,
+ {time_addr, time_size / Memory::PageSize}, Memory::MemoryPermission::None,
+ Memory::MemoryPermission::Read, time_addr, time_size, "Time:SharedMemory");
+
+ // Allocate slab heaps
+ user_slab_heap_pages = std::make_unique<Memory::SlabHeap<Memory::Page>>();
+
+ // Initialize slab heaps
+ constexpr u64 user_slab_heap_size{0x3de000};
+ user_slab_heap_pages->Initialize(
+ system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
+ user_slab_heap_size);
+ }
+
std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
@@ -251,12 +313,11 @@ struct KernelCore::Impl {
std::shared_ptr<ResourceLimit> system_resource_limit;
- std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type;
std::shared_ptr<Core::Timing::EventType> preemption_event;
// This is the kernel's handle table or supervisor handle table which
// stores all the objects in place.
- Kernel::HandleTable global_handle_table;
+ HandleTable global_handle_table;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
@@ -266,10 +327,33 @@ struct KernelCore::Impl {
std::vector<Kernel::PhysicalCore> cores;
// 0-3 IDs represent core threads, >3 represent others
- std::unordered_map<std::thread::id, u32> host_thread_ids;
- u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
- std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
- std::mutex register_thread_mutex;
+ std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
+
+ // Number of host threads is a relatively high number to avoid overflowing
+ static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
+ std::atomic<size_t> num_host_threads{0};
+ std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
+ register_host_thread_keys{};
+ std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
+
+ // Kernel memory management
+ std::unique_ptr<Memory::MemoryManager> memory_manager;
+ std::unique_ptr<Memory::SlabHeap<Memory::Page>> user_slab_heap_pages;
+
+ // Shared memory for services
+ std::shared_ptr<Kernel::SharedMemory> hid_shared_mem;
+ std::shared_ptr<Kernel::SharedMemory> font_shared_mem;
+ std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
+ std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
+
+ std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
+ std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
+ std::array<std::unique_ptr<Kernel::Scheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
+
+ bool is_multicore{};
+ std::thread::id single_core_thread_id{};
+
+ std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
// System context
Core::System& system;
@@ -280,6 +364,10 @@ KernelCore::~KernelCore() {
Shutdown();
}
+void KernelCore::SetMulticore(bool is_multicore) {
+ impl->SetMulticore(is_multicore);
+}
+
void KernelCore::Initialize() {
impl->Initialize(*this);
}
@@ -325,11 +413,11 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
}
Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) {
- return impl->cores[id].Scheduler();
+ return *impl->schedulers[id];
}
const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const {
- return impl->cores[id].Scheduler();
+ return *impl->schedulers[id];
}
Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
@@ -340,6 +428,39 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
return impl->cores[id];
}
+Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
+ u32 core_id = impl->GetCurrentHostThreadID();
+ ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
+ return impl->cores[core_id];
+}
+
+const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
+ u32 core_id = impl->GetCurrentHostThreadID();
+ ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
+ return impl->cores[core_id];
+}
+
+Kernel::Scheduler& KernelCore::CurrentScheduler() {
+ u32 core_id = impl->GetCurrentHostThreadID();
+ ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
+ return *impl->schedulers[core_id];
+}
+
+const Kernel::Scheduler& KernelCore::CurrentScheduler() const {
+ u32 core_id = impl->GetCurrentHostThreadID();
+ ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
+ return *impl->schedulers[core_id];
+}
+
+std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() {
+ return impl->interrupts;
+}
+
+const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts()
+ const {
+ return impl->interrupts;
+}
+
Kernel::Synchronization& KernelCore::Synchronization() {
return impl->synchronization;
}
@@ -365,15 +486,17 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
}
void KernelCore::InvalidateAllInstructionCaches() {
- for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) {
- PhysicalCore(i).ArmInterface().ClearInstructionCache();
+ auto& threads = GlobalScheduler().GetThreadList();
+ for (auto& thread : threads) {
+ if (!thread->IsHLEThread()) {
+ auto& arm_interface = thread->ArmInterface();
+ arm_interface.ClearInstructionCache();
+ }
}
}
void KernelCore::PrepareReschedule(std::size_t id) {
- if (id < impl->global_scheduler.CpuCoresCount()) {
- impl->cores[id].Stop();
- }
+ // TODO: Reimplement, this
}
void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) {
@@ -409,10 +532,6 @@ u64 KernelCore::CreateNewUserProcessID() {
return impl->next_user_process_id++;
}
-const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallbackEventType() const {
- return impl->thread_wakeup_event_type;
-}
-
Kernel::HandleTable& KernelCore::GlobalHandleTable() {
return impl->global_handle_table;
}
@@ -437,4 +556,82 @@ Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
return impl->GetCurrentEmuThreadID();
}
+Memory::MemoryManager& KernelCore::MemoryManager() {
+ return *impl->memory_manager;
+}
+
+const Memory::MemoryManager& KernelCore::MemoryManager() const {
+ return *impl->memory_manager;
+}
+
+Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() {
+ return *impl->user_slab_heap_pages;
+}
+
+const Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() const {
+ return *impl->user_slab_heap_pages;
+}
+
+Kernel::SharedMemory& KernelCore::GetHidSharedMem() {
+ return *impl->hid_shared_mem;
+}
+
+const Kernel::SharedMemory& KernelCore::GetHidSharedMem() const {
+ return *impl->hid_shared_mem;
+}
+
+Kernel::SharedMemory& KernelCore::GetFontSharedMem() {
+ return *impl->font_shared_mem;
+}
+
+const Kernel::SharedMemory& KernelCore::GetFontSharedMem() const {
+ return *impl->font_shared_mem;
+}
+
+Kernel::SharedMemory& KernelCore::GetIrsSharedMem() {
+ return *impl->irs_shared_mem;
+}
+
+const Kernel::SharedMemory& KernelCore::GetIrsSharedMem() const {
+ return *impl->irs_shared_mem;
+}
+
+Kernel::SharedMemory& KernelCore::GetTimeSharedMem() {
+ return *impl->time_shared_mem;
+}
+
+const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const {
+ return *impl->time_shared_mem;
+}
+
+void KernelCore::Suspend(bool in_suspention) {
+ const bool should_suspend = exception_exited || in_suspention;
+ {
+ SchedulerLock lock(*this);
+ ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep;
+ for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ impl->suspend_threads[i]->SetStatus(status);
+ }
+ }
+}
+
+bool KernelCore::IsMulticore() const {
+ return impl->is_multicore;
+}
+
+void KernelCore::ExceptionalExit() {
+ exception_exited = true;
+ Suspend(true);
+}
+
+void KernelCore::EnterSVCProfile() {
+ std::size_t core = impl->GetCurrentHostThreadID();
+ impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
+}
+
+void KernelCore::ExitSVCProfile() {
+ std::size_t core = impl->GetCurrentHostThreadID();
+ MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c4f78ab71..16285c3f0 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,14 +4,18 @@
#pragma once
+#include <array>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
+#include "core/arm/cpu_interrupt_handler.h"
+#include "core/hardware_properties.h"
+#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/object.h"
namespace Core {
-struct EmuThreadHandle;
+class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -23,6 +27,12 @@ struct EventType;
namespace Kernel {
+namespace Memory {
+class MemoryManager;
+template <typename T>
+class SlabHeap;
+} // namespace Memory
+
class AddressArbiter;
class ClientPort;
class GlobalScheduler;
@@ -31,6 +41,7 @@ class PhysicalCore;
class Process;
class ResourceLimit;
class Scheduler;
+class SharedMemory;
class Synchronization;
class Thread;
class TimeManager;
@@ -57,6 +68,9 @@ public:
KernelCore(KernelCore&&) = delete;
KernelCore& operator=(KernelCore&&) = delete;
+ /// Sets if emulation is multicore or single core, must be set before Initialize
+ void SetMulticore(bool is_multicore);
+
/// Resets the kernel to a clean slate for use.
void Initialize();
@@ -102,6 +116,18 @@ public:
/// Gets the an instance of the respective physical CPU core.
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
+ /// Gets the sole instance of the Scheduler at the current running core.
+ Kernel::Scheduler& CurrentScheduler();
+
+ /// Gets the sole instance of the Scheduler at the current running core.
+ const Kernel::Scheduler& CurrentScheduler() const;
+
+ /// Gets the an instance of the current physical CPU core.
+ Kernel::PhysicalCore& CurrentPhysicalCore();
+
+ /// Gets the an instance of the current physical CPU core.
+ const Kernel::PhysicalCore& CurrentPhysicalCore() const;
+
/// Gets the an instance of the Synchronization Interface.
Kernel::Synchronization& Synchronization();
@@ -121,6 +147,10 @@ public:
const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
+ std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
+
+ const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
+
void InvalidateAllInstructionCaches();
/// Adds a port to the named port table
@@ -147,6 +177,54 @@ public:
/// Register the current thread as a non CPU core thread.
void RegisterHostThread();
+ /// Gets the virtual memory manager for the kernel.
+ Memory::MemoryManager& MemoryManager();
+
+ /// Gets the virtual memory manager for the kernel.
+ const Memory::MemoryManager& MemoryManager() const;
+
+ /// Gets the slab heap allocated for user space pages.
+ Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages();
+
+ /// Gets the slab heap allocated for user space pages.
+ const Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages() const;
+
+ /// Gets the shared memory object for HID services.
+ Kernel::SharedMemory& GetHidSharedMem();
+
+ /// Gets the shared memory object for HID services.
+ const Kernel::SharedMemory& GetHidSharedMem() const;
+
+ /// Gets the shared memory object for font services.
+ Kernel::SharedMemory& GetFontSharedMem();
+
+ /// Gets the shared memory object for font services.
+ const Kernel::SharedMemory& GetFontSharedMem() const;
+
+ /// Gets the shared memory object for IRS services.
+ Kernel::SharedMemory& GetIrsSharedMem();
+
+ /// Gets the shared memory object for IRS services.
+ const Kernel::SharedMemory& GetIrsSharedMem() const;
+
+ /// Gets the shared memory object for Time services.
+ Kernel::SharedMemory& GetTimeSharedMem();
+
+ /// Gets the shared memory object for Time services.
+ const Kernel::SharedMemory& GetTimeSharedMem() const;
+
+ /// Suspend/unsuspend the OS.
+ void Suspend(bool in_suspention);
+
+ /// Exceptional exit the OS.
+ void ExceptionalExit();
+
+ bool IsMulticore() const;
+
+ void EnterSVCProfile();
+
+ void ExitSVCProfile();
+
private:
friend class Object;
friend class Process;
@@ -164,9 +242,6 @@ private:
/// Creates a new thread ID, incrementing the internal thread ID counter.
u64 CreateNewThreadID();
- /// Retrieves the event type used for thread wakeup callbacks.
- const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const;
-
/// Provides a reference to the global handle table.
Kernel::HandleTable& GlobalHandleTable();
@@ -175,6 +250,7 @@ private:
struct Impl;
std::unique_ptr<Impl> impl;
+ bool exception_exited{};
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/memory/address_space_info.cpp b/src/core/hle/kernel/memory/address_space_info.cpp
new file mode 100644
index 000000000..e4288cab4
--- /dev/null
+++ b/src/core/hle/kernel/memory/address_space_info.cpp
@@ -0,0 +1,117 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// This file references various implementation details from Atmosphere, an open-source firmware for
+// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+
+#include <array>
+
+#include "common/assert.h"
+#include "core/hle/kernel/memory/address_space_info.h"
+
+namespace Kernel::Memory {
+
+namespace {
+
+enum : u64 {
+ Size_1_MB = 0x100000,
+ Size_2_MB = 2 * Size_1_MB,
+ Size_128_MB = 128 * Size_1_MB,
+ Size_1_GB = 0x40000000,
+ Size_2_GB = 2 * Size_1_GB,
+ Size_4_GB = 4 * Size_1_GB,
+ Size_6_GB = 6 * Size_1_GB,
+ Size_64_GB = 64 * Size_1_GB,
+ Size_512_GB = 512 * Size_1_GB,
+ Invalid = std::numeric_limits<u64>::max(),
+};
+
+// clang-format off
+constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{
+ { .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = AddressSpaceInfo::Type::Is32Bit, },
+ { .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
+ { .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Heap, },
+ { .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Alias, },
+ { .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Is32Bit, },
+ { .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
+ { .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
+ { .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Alias, },
+ { .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Large64Bit, },
+ { .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Is32Bit },
+ { .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
+ { .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Alias, },
+ { .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = AddressSpaceInfo::Type::Stack, },
+}};
+// clang-format on
+
+constexpr bool IsAllowedIndexForAddress(std::size_t index) {
+ return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid;
+}
+
+using IndexArray = std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>;
+
+constexpr IndexArray AddressSpaceIndices32Bit{
+ 0, 1, 0, 2, 0, 3,
+};
+
+constexpr IndexArray AddressSpaceIndices36Bit{
+ 4, 5, 4, 6, 4, 7,
+};
+
+constexpr IndexArray AddressSpaceIndices39Bit{
+ 9, 8, 8, 10, 12, 11,
+};
+
+constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) {
+ return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
+ type != AddressSpaceInfo::Type::Stack;
+}
+
+constexpr bool IsAllowed36BitType(AddressSpaceInfo::Type type) {
+ return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
+ type != AddressSpaceInfo::Type::Stack;
+}
+
+constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) {
+ return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Small64Bit;
+}
+
+} // namespace
+
+u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
+ const std::size_t index{static_cast<std::size_t>(type)};
+ switch (width) {
+ case 32:
+ ASSERT(IsAllowed32BitType(type));
+ ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index]));
+ return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].address;
+ case 36:
+ ASSERT(IsAllowed36BitType(type));
+ ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index]));
+ return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].address;
+ case 39:
+ ASSERT(IsAllowed39BitType(type));
+ ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
+ return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
+ }
+ UNREACHABLE();
+}
+
+std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
+ const std::size_t index{static_cast<std::size_t>(type)};
+ switch (width) {
+ case 32:
+ ASSERT(IsAllowed32BitType(type));
+ return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].size;
+ case 36:
+ ASSERT(IsAllowed36BitType(type));
+ return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].size;
+ case 39:
+ ASSERT(IsAllowed39BitType(type));
+ return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
+ }
+ UNREACHABLE();
+}
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/address_space_info.h b/src/core/hle/kernel/memory/address_space_info.h
new file mode 100644
index 000000000..a4e6e91e5
--- /dev/null
+++ b/src/core/hle/kernel/memory/address_space_info.h
@@ -0,0 +1,34 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// This file references various implementation details from Atmosphere, an open-source firmware for
+// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Kernel::Memory {
+
+struct AddressSpaceInfo final {
+ enum class Type : u32 {
+ Is32Bit = 0,
+ Small64Bit = 1,
+ Large64Bit = 2,
+ Heap = 3,
+ Stack = 4,
+ Alias = 5,
+ Count,
+ };
+
+ static u64 GetAddressSpaceStart(std::size_t width, Type type);
+ static std::size_t GetAddressSpaceSize(std::size_t width, Type type);
+
+ const std::size_t bit_width{};
+ const std::size_t address{};
+ const std::size_t size{};
+ const Type type{};
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h
new file mode 100644
index 000000000..9d7839d08
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_block.h
@@ -0,0 +1,335 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// This file references various implementation details from Atmosphere, an open-source firmware for
+// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+
+#pragma once
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/memory/memory_types.h"
+#include "core/hle/kernel/svc_types.h"
+
+namespace Kernel::Memory {
+
+enum class MemoryState : u32 {
+ None = 0,
+ Mask = 0xFF,
+ All = ~None,
+
+ FlagCanReprotect = (1 << 8),
+ FlagCanDebug = (1 << 9),
+ FlagCanUseIpc = (1 << 10),
+ FlagCanUseNonDeviceIpc = (1 << 11),
+ FlagCanUseNonSecureIpc = (1 << 12),
+ FlagMapped = (1 << 13),
+ FlagCode = (1 << 14),
+ FlagCanAlias = (1 << 15),
+ FlagCanCodeAlias = (1 << 16),
+ FlagCanTransfer = (1 << 17),
+ FlagCanQueryPhysical = (1 << 18),
+ FlagCanDeviceMap = (1 << 19),
+ FlagCanAlignedDeviceMap = (1 << 20),
+ FlagCanIpcUserBuffer = (1 << 21),
+ FlagReferenceCounted = (1 << 22),
+ FlagCanMapProcess = (1 << 23),
+ FlagCanChangeAttribute = (1 << 24),
+ FlagCanCodeMemory = (1 << 25),
+
+ FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
+ FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
+ FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer |
+ FlagReferenceCounted | FlagCanChangeAttribute,
+
+ FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
+ FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap |
+ FlagCanAlignedDeviceMap | FlagReferenceCounted,
+
+ FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap,
+
+ Free = static_cast<u32>(Svc::MemoryState::Free),
+ Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped,
+ Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
+ Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
+ CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
+ FlagCanCodeMemory,
+ Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted,
+ Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
+
+ AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
+ FlagCanCodeAlias,
+ AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
+ FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
+
+ Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
+ FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+ Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
+ FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+ ThreadLocal =
+ static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
+
+ Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
+ FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
+ FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+ SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
+ FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+ SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
+ FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+ Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible),
+
+ NonSecureIpc = static_cast<u32>(Svc::MemoryState::NonSecureIpc) | FlagsMisc |
+ FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+ NonDeviceIpc =
+ static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
+
+ Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
+
+ GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
+ FlagReferenceCounted | FlagCanDebug,
+ CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
+
+static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000);
+static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001);
+static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002);
+static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03);
+static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04);
+static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05);
+static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006);
+static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08);
+static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
+static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
+static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
+static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
+static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D);
+static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E);
+static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
+static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
+static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
+static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812);
+static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013);
+static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214);
+static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015);
+
+enum class MemoryPermission : u8 {
+ None = 0,
+ Mask = static_cast<u8>(~None),
+
+ Read = 1 << 0,
+ Write = 1 << 1,
+ Execute = 1 << 2,
+
+ ReadAndWrite = Read | Write,
+ ReadAndExecute = Read | Execute,
+
+ UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
+ Svc::MemoryPermission::Execute),
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
+
+enum class MemoryAttribute : u8 {
+ None = 0x00,
+ Mask = 0x7F,
+ All = Mask,
+ DontCareMask = 0x80,
+
+ Locked = static_cast<u8>(Svc::MemoryAttribute::Locked),
+ IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
+ DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
+ Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
+
+ IpcAndDeviceMapped = IpcLocked | DeviceShared,
+ LockedAndIpcLocked = Locked | IpcLocked,
+ DeviceSharedAndUncached = DeviceShared | Uncached
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
+
+static_assert((static_cast<u8>(MemoryAttribute::Mask) &
+ static_cast<u8>(MemoryAttribute::DontCareMask)) == 0);
+
+struct MemoryInfo {
+ VAddr addr{};
+ std::size_t size{};
+ MemoryState state{};
+ MemoryPermission perm{};
+ MemoryAttribute attribute{};
+ MemoryPermission original_perm{};
+ u16 ipc_lock_count{};
+ u16 device_use_count{};
+
+ constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
+ return {
+ addr,
+ size,
+ static_cast<Svc::MemoryState>(state & MemoryState::Mask),
+ static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask),
+ static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask),
+ ipc_lock_count,
+ device_use_count,
+ };
+ }
+
+ constexpr VAddr GetAddress() const {
+ return addr;
+ }
+ constexpr std::size_t GetSize() const {
+ return size;
+ }
+ constexpr std::size_t GetNumPages() const {
+ return GetSize() / PageSize;
+ }
+ constexpr VAddr GetEndAddress() const {
+ return GetAddress() + GetSize();
+ }
+ constexpr VAddr GetLastAddress() const {
+ return GetEndAddress() - 1;
+ }
+};
+
+class MemoryBlock final {
+ friend class MemoryBlockManager;
+
+private:
+ VAddr addr{};
+ std::size_t num_pages{};
+ MemoryState state{MemoryState::None};
+ u16 ipc_lock_count{};
+ u16 device_use_count{};
+ MemoryPermission perm{MemoryPermission::None};
+ MemoryPermission original_perm{MemoryPermission::None};
+ MemoryAttribute attribute{MemoryAttribute::None};
+
+public:
+ static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) {
+ if (lhs.GetAddress() < rhs.GetAddress()) {
+ return -1;
+ } else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+public:
+ constexpr MemoryBlock() = default;
+ constexpr MemoryBlock(VAddr addr, std::size_t num_pages, MemoryState state,
+ MemoryPermission perm, MemoryAttribute attribute)
+ : addr{addr}, num_pages(num_pages), state{state}, perm{perm}, attribute{attribute} {}
+
+ constexpr VAddr GetAddress() const {
+ return addr;
+ }
+
+ constexpr std::size_t GetNumPages() const {
+ return num_pages;
+ }
+
+ constexpr std::size_t GetSize() const {
+ return GetNumPages() * PageSize;
+ }
+
+ constexpr VAddr GetEndAddress() const {
+ return GetAddress() + GetSize();
+ }
+
+ constexpr VAddr GetLastAddress() const {
+ return GetEndAddress() - 1;
+ }
+
+ constexpr MemoryInfo GetMemoryInfo() const {
+ return {
+ GetAddress(), GetSize(), state, perm,
+ attribute, original_perm, ipc_lock_count, device_use_count,
+ };
+ }
+
+ void ShareToDevice(MemoryPermission /*new_perm*/) {
+ ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared ||
+ device_use_count == 0);
+ attribute |= MemoryAttribute::DeviceShared;
+ const u16 new_use_count{++device_use_count};
+ ASSERT(new_use_count > 0);
+ }
+
+ void UnshareToDevice(MemoryPermission /*new_perm*/) {
+ ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared);
+ const u16 prev_use_count{device_use_count--};
+ ASSERT(prev_use_count > 0);
+ if (prev_use_count == 1) {
+ attribute &= ~MemoryAttribute::DeviceShared;
+ }
+ }
+
+private:
+ constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
+ constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
+ MemoryAttribute::IpcLocked |
+ MemoryAttribute::DeviceShared};
+ return state == s && perm == p &&
+ (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
+ }
+
+ constexpr bool HasSameProperties(const MemoryBlock& rhs) const {
+ return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm &&
+ attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count &&
+ device_use_count == rhs.device_use_count;
+ }
+
+ constexpr bool Contains(VAddr start) const {
+ return GetAddress() <= start && start <= GetEndAddress();
+ }
+
+ constexpr void Add(std::size_t count) {
+ ASSERT(count > 0);
+ ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1);
+
+ num_pages += count;
+ }
+
+ constexpr void Update(MemoryState new_state, MemoryPermission new_perm,
+ MemoryAttribute new_attribute) {
+ ASSERT(original_perm == MemoryPermission::None);
+ ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None);
+
+ state = new_state;
+ perm = new_perm;
+
+ attribute = static_cast<MemoryAttribute>(
+ new_attribute |
+ (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared)));
+ }
+
+ constexpr MemoryBlock Split(VAddr split_addr) {
+ ASSERT(GetAddress() < split_addr);
+ ASSERT(Contains(split_addr));
+ ASSERT(Common::IsAligned(split_addr, PageSize));
+
+ MemoryBlock block;
+ block.addr = addr;
+ block.num_pages = (split_addr - GetAddress()) / PageSize;
+ block.state = state;
+ block.ipc_lock_count = ipc_lock_count;
+ block.device_use_count = device_use_count;
+ block.perm = perm;
+ block.original_perm = original_perm;
+ block.attribute = attribute;
+
+ addr = split_addr;
+ num_pages -= block.num_pages;
+
+ return block;
+ }
+};
+static_assert(std::is_trivially_destructible<MemoryBlock>::value);
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_block_manager.cpp b/src/core/hle/kernel/memory/memory_block_manager.cpp
new file mode 100644
index 000000000..0732fa5a1
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_block_manager.cpp
@@ -0,0 +1,223 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/memory/memory_block_manager.h"
+#include "core/hle/kernel/memory/memory_types.h"
+
+namespace Kernel::Memory {
+
+MemoryBlockManager::MemoryBlockManager(VAddr start_addr, VAddr end_addr)
+ : start_addr{start_addr}, end_addr{end_addr} {
+ const u64 num_pages{(end_addr - start_addr) / PageSize};
+ memory_block_tree.emplace_back(start_addr, num_pages, MemoryState::Free, MemoryPermission::None,
+ MemoryAttribute::None);
+}
+
+MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) {
+ auto node{memory_block_tree.begin()};
+ while (node != end()) {
+ const VAddr end_addr{node->GetNumPages() * PageSize + node->GetAddress()};
+ if (node->GetAddress() <= addr && end_addr - 1 >= addr) {
+ return node;
+ }
+ node = std::next(node);
+ }
+ return end();
+}
+
+VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
+ std::size_t num_pages, std::size_t align, std::size_t offset,
+ std::size_t guard_pages) {
+ if (num_pages == 0) {
+ return {};
+ }
+
+ const VAddr region_end{region_start + region_num_pages * PageSize};
+ const VAddr region_last{region_end - 1};
+ for (auto it{FindIterator(region_start)}; it != memory_block_tree.cend(); it++) {
+ const auto info{it->GetMemoryInfo()};
+ if (region_last < info.GetAddress()) {
+ break;
+ }
+
+ if (info.state != MemoryState::Free) {
+ continue;
+ }
+
+ VAddr area{(info.GetAddress() <= region_start) ? region_start : info.GetAddress()};
+ area += guard_pages * PageSize;
+
+ const VAddr offset_area{Common::AlignDown(area, align) + offset};
+ area = (area <= offset_area) ? offset_area : offset_area + align;
+
+ const VAddr area_end{area + num_pages * PageSize + guard_pages * PageSize};
+ const VAddr area_last{area_end - 1};
+
+ if (info.GetAddress() <= area && area < area_last && area_last <= region_last &&
+ area_last <= info.GetLastAddress()) {
+ return area;
+ }
+ }
+
+ return {};
+}
+
+void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
+ MemoryPermission prev_perm, MemoryAttribute prev_attribute,
+ MemoryState state, MemoryPermission perm,
+ MemoryAttribute attribute) {
+ const VAddr end_addr{addr + num_pages * PageSize};
+ iterator node{memory_block_tree.begin()};
+
+ prev_attribute |= MemoryAttribute::IpcAndDeviceMapped;
+
+ while (node != memory_block_tree.end()) {
+ MemoryBlock* block{&(*node)};
+ iterator next_node{std::next(node)};
+ const VAddr cur_addr{block->GetAddress()};
+ const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
+
+ if (addr < cur_end_addr && cur_addr < end_addr) {
+ if (!block->HasProperties(prev_state, prev_perm, prev_attribute)) {
+ node = next_node;
+ continue;
+ }
+
+ iterator new_node{node};
+ if (addr > cur_addr) {
+ memory_block_tree.insert(node, block->Split(addr));
+ }
+
+ if (end_addr < cur_end_addr) {
+ new_node = memory_block_tree.insert(node, block->Split(end_addr));
+ }
+
+ new_node->Update(state, perm, attribute);
+
+ MergeAdjacent(new_node, next_node);
+ }
+
+ if (cur_end_addr - 1 >= end_addr - 1) {
+ break;
+ }
+
+ node = next_node;
+ }
+}
+
+void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState state,
+ MemoryPermission perm, MemoryAttribute attribute) {
+ const VAddr end_addr{addr + num_pages * PageSize};
+ iterator node{memory_block_tree.begin()};
+
+ while (node != memory_block_tree.end()) {
+ MemoryBlock* block{&(*node)};
+ iterator next_node{std::next(node)};
+ const VAddr cur_addr{block->GetAddress()};
+ const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
+
+ if (addr < cur_end_addr && cur_addr < end_addr) {
+ iterator new_node{node};
+
+ if (addr > cur_addr) {
+ memory_block_tree.insert(node, block->Split(addr));
+ }
+
+ if (end_addr < cur_end_addr) {
+ new_node = memory_block_tree.insert(node, block->Split(end_addr));
+ }
+
+ new_node->Update(state, perm, attribute);
+
+ MergeAdjacent(new_node, next_node);
+ }
+
+ if (cur_end_addr - 1 >= end_addr - 1) {
+ break;
+ }
+
+ node = next_node;
+ }
+}
+
+void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
+ MemoryPermission perm) {
+ const VAddr end_addr{addr + num_pages * PageSize};
+ iterator node{memory_block_tree.begin()};
+
+ while (node != memory_block_tree.end()) {
+ MemoryBlock* block{&(*node)};
+ iterator next_node{std::next(node)};
+ const VAddr cur_addr{block->GetAddress()};
+ const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
+
+ if (addr < cur_end_addr && cur_addr < end_addr) {
+ iterator new_node{node};
+
+ if (addr > cur_addr) {
+ memory_block_tree.insert(node, block->Split(addr));
+ }
+
+ if (end_addr < cur_end_addr) {
+ new_node = memory_block_tree.insert(node, block->Split(end_addr));
+ }
+
+ lock_func(new_node, perm);
+
+ MergeAdjacent(new_node, next_node);
+ }
+
+ if (cur_end_addr - 1 >= end_addr - 1) {
+ break;
+ }
+
+ node = next_node;
+ }
+}
+
+void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
+ const_iterator it{FindIterator(start)};
+ MemoryInfo info{};
+ do {
+ info = it->GetMemoryInfo();
+ func(info);
+ it = std::next(it);
+ } while (info.addr + info.size - 1 < end - 1 && it != cend());
+}
+
+void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
+ MemoryBlock* block{&(*it)};
+
+ auto EraseIt = [&](const iterator it_to_erase) {
+ if (next_it == it_to_erase) {
+ next_it = std::next(next_it);
+ }
+ memory_block_tree.erase(it_to_erase);
+ };
+
+ if (it != memory_block_tree.begin()) {
+ MemoryBlock* prev{&(*std::prev(it))};
+
+ if (block->HasSameProperties(*prev)) {
+ const iterator prev_it{std::prev(it)};
+
+ prev->Add(block->GetNumPages());
+ EraseIt(it);
+
+ it = prev_it;
+ block = prev;
+ }
+ }
+
+ if (it != cend()) {
+ const MemoryBlock* const next{&(*std::next(it))};
+
+ if (block->HasSameProperties(*next)) {
+ block->Add(next->GetNumPages());
+ EraseIt(std::next(it));
+ }
+ }
+}
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_block_manager.h b/src/core/hle/kernel/memory/memory_block_manager.h
new file mode 100644
index 000000000..6e1d41075
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_block_manager.h
@@ -0,0 +1,66 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+#include <list>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/memory/memory_block.h"
+
+namespace Kernel::Memory {
+
+class MemoryBlockManager final {
+public:
+ using MemoryBlockTree = std::list<MemoryBlock>;
+ using iterator = MemoryBlockTree::iterator;
+ using const_iterator = MemoryBlockTree::const_iterator;
+
+public:
+ MemoryBlockManager(VAddr start_addr, VAddr end_addr);
+
+ iterator end() {
+ return memory_block_tree.end();
+ }
+ const_iterator end() const {
+ return memory_block_tree.end();
+ }
+ const_iterator cend() const {
+ return memory_block_tree.cend();
+ }
+
+ iterator FindIterator(VAddr addr);
+
+ VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
+ std::size_t align, std::size_t offset, std::size_t guard_pages);
+
+ void Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
+ MemoryPermission prev_perm, MemoryAttribute prev_attribute, MemoryState state,
+ MemoryPermission perm, MemoryAttribute attribute);
+
+ void Update(VAddr addr, std::size_t num_pages, MemoryState state,
+ MemoryPermission perm = MemoryPermission::None,
+ MemoryAttribute attribute = MemoryAttribute::None);
+
+ using LockFunc = std::function<void(iterator, MemoryPermission)>;
+ void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm);
+
+ using IterateFunc = std::function<void(const MemoryInfo&)>;
+ void IterateForRange(VAddr start, VAddr end, IterateFunc&& func);
+
+ MemoryBlock& FindBlock(VAddr addr) {
+ return *FindIterator(addr);
+ }
+
+private:
+ void MergeAdjacent(iterator it, iterator& next_it);
+
+ const VAddr start_addr;
+ const VAddr end_addr;
+
+ MemoryBlockTree memory_block_tree;
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h
new file mode 100644
index 000000000..9b3d6267a
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_layout.h
@@ -0,0 +1,71 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Kernel::Memory {
+
+class MemoryRegion final {
+ friend class MemoryLayout;
+
+public:
+ constexpr PAddr StartAddress() const {
+ return start_address;
+ }
+
+ constexpr PAddr EndAddress() const {
+ return end_address;
+ }
+
+private:
+ constexpr MemoryRegion() = default;
+ constexpr MemoryRegion(PAddr start_address, PAddr end_address)
+ : start_address{start_address}, end_address{end_address} {}
+
+ const PAddr start_address{};
+ const PAddr end_address{};
+};
+
+class MemoryLayout final {
+public:
+ constexpr const MemoryRegion& Application() const {
+ return application;
+ }
+
+ constexpr const MemoryRegion& Applet() const {
+ return applet;
+ }
+
+ constexpr const MemoryRegion& System() const {
+ return system;
+ }
+
+ static constexpr MemoryLayout GetDefaultLayout() {
+ constexpr std::size_t application_size{0xcd500000};
+ constexpr std::size_t applet_size{0x1fb00000};
+ constexpr PAddr application_start_address{Core::DramMemoryMap::End - application_size};
+ constexpr PAddr application_end_address{Core::DramMemoryMap::End};
+ constexpr PAddr applet_start_address{application_start_address - applet_size};
+ constexpr PAddr applet_end_address{applet_start_address + applet_size};
+ constexpr PAddr system_start_address{Core::DramMemoryMap::SlabHeapEnd};
+ constexpr PAddr system_end_address{applet_start_address};
+ return {application_start_address, application_end_address, applet_start_address,
+ applet_end_address, system_start_address, system_end_address};
+ }
+
+private:
+ constexpr MemoryLayout(PAddr application_start_address, std::size_t application_size,
+ PAddr applet_start_address, std::size_t applet_size,
+ PAddr system_start_address, std::size_t system_size)
+ : application{application_start_address, application_size},
+ applet{applet_start_address, applet_size}, system{system_start_address, system_size} {}
+
+ const MemoryRegion application;
+ const MemoryRegion applet;
+ const MemoryRegion system;
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_manager.cpp b/src/core/hle/kernel/memory/memory_manager.cpp
new file mode 100644
index 000000000..acf13585c
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_manager.cpp
@@ -0,0 +1,175 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/scope_exit.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/memory/memory_manager.h"
+#include "core/hle/kernel/memory/page_linked_list.h"
+
+namespace Kernel::Memory {
+
+std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
+ const auto size{end_address - start_address};
+
+ // Calculate metadata sizes
+ const auto ref_count_size{(size / PageSize) * sizeof(u16)};
+ const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)};
+ const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)};
+ const auto page_heap_size{PageHeap::CalculateMetadataOverheadSize(size)};
+ const auto total_metadata_size{manager_size + page_heap_size};
+ ASSERT(manager_size <= total_metadata_size);
+ ASSERT(Common::IsAligned(total_metadata_size, PageSize));
+
+ // Setup region
+ pool = new_pool;
+
+ // Initialize the manager's KPageHeap
+ heap.Initialize(start_address, size, page_heap_size);
+
+ // Free the memory to the heap
+ heap.Free(start_address, size / PageSize);
+
+ // Update the heap's used size
+ heap.UpdateUsedSize();
+
+ return total_metadata_size;
+}
+
+void MemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
+ ASSERT(pool < Pool::Count);
+ managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address);
+}
+
+VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool,
+ Direction dir) {
+ // Early return if we're allocating no pages
+ if (num_pages == 0) {
+ return {};
+ }
+
+ // Lock the pool that we're allocating from
+ const auto pool_index{static_cast<std::size_t>(pool)};
+ std::lock_guard lock{pool_locks[pool_index]};
+
+ // Choose a heap based on our page size request
+ const s32 heap_index{PageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
+
+ // Loop, trying to iterate from each block
+ // TODO (bunnei): Support multiple managers
+ Impl& chosen_manager{managers[pool_index]};
+ VAddr allocated_block{chosen_manager.AllocateBlock(heap_index)};
+
+ // If we failed to allocate, quit now
+ if (!allocated_block) {
+ return {};
+ }
+
+ // If we allocated more than we need, free some
+ const auto allocated_pages{PageHeap::GetBlockNumPages(heap_index)};
+ if (allocated_pages > num_pages) {
+ chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
+ }
+
+ return allocated_block;
+}
+
+ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
+ Direction dir) {
+ ASSERT(page_list.GetNumPages() == 0);
+
+ // Early return if we're allocating no pages
+ if (num_pages == 0) {
+ return RESULT_SUCCESS;
+ }
+
+ // Lock the pool that we're allocating from
+ const auto pool_index{static_cast<std::size_t>(pool)};
+ std::lock_guard lock{pool_locks[pool_index]};
+
+ // Choose a heap based on our page size request
+ const s32 heap_index{PageHeap::GetBlockIndex(num_pages)};
+ if (heap_index < 0) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ // TODO (bunnei): Support multiple managers
+ Impl& chosen_manager{managers[pool_index]};
+
+ // Ensure that we don't leave anything un-freed
+ auto group_guard = detail::ScopeExit([&] {
+ for (const auto& it : page_list.Nodes()) {
+ const auto min_num_pages{std::min<size_t>(
+ it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
+ chosen_manager.Free(it.GetAddress(), min_num_pages);
+ }
+ });
+
+ // Keep allocating until we've allocated all our pages
+ for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) {
+ const auto pages_per_alloc{PageHeap::GetBlockNumPages(index)};
+
+ while (num_pages >= pages_per_alloc) {
+ // Allocate a block
+ VAddr allocated_block{chosen_manager.AllocateBlock(index)};
+ if (!allocated_block) {
+ break;
+ }
+
+ // Safely add it to our group
+ {
+ auto block_guard = detail::ScopeExit(
+ [&] { chosen_manager.Free(allocated_block, pages_per_alloc); });
+
+ if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)};
+ result.IsError()) {
+ return result;
+ }
+
+ block_guard.Cancel();
+ }
+
+ num_pages -= pages_per_alloc;
+ }
+ }
+
+ // Only succeed if we allocated as many pages as we wanted
+ if (num_pages) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ // We succeeded!
+ group_guard.Cancel();
+ return RESULT_SUCCESS;
+}
+
+ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
+ Direction dir) {
+ // Early return if we're freeing no pages
+ if (!num_pages) {
+ return RESULT_SUCCESS;
+ }
+
+ // Lock the pool that we're freeing from
+ const auto pool_index{static_cast<std::size_t>(pool)};
+ std::lock_guard lock{pool_locks[pool_index]};
+
+ // TODO (bunnei): Support multiple managers
+ Impl& chosen_manager{managers[pool_index]};
+
+ // Free all of the pages
+ for (const auto& it : page_list.Nodes()) {
+ const auto min_num_pages{std::min<size_t>(
+ it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
+ chosen_manager.Free(it.GetAddress(), min_num_pages);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_manager.h b/src/core/hle/kernel/memory/memory_manager.h
new file mode 100644
index 000000000..3cf444857
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_manager.h
@@ -0,0 +1,96 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/memory/page_heap.h"
+#include "core/hle/result.h"
+
+namespace Kernel::Memory {
+
+class PageLinkedList;
+
+class MemoryManager final : NonCopyable {
+public:
+ enum class Pool : u32 {
+ Application = 0,
+ Applet = 1,
+ System = 2,
+ SystemNonSecure = 3,
+
+ Count,
+
+ Shift = 4,
+ Mask = (0xF << Shift),
+ };
+
+ enum class Direction : u32 {
+ FromFront = 0,
+ FromBack = 1,
+
+ Shift = 0,
+ Mask = (0xF << Shift),
+ };
+
+ MemoryManager() = default;
+
+ constexpr std::size_t GetSize(Pool pool) const {
+ return managers[static_cast<std::size_t>(pool)].GetSize();
+ }
+
+ void InitializeManager(Pool pool, u64 start_address, u64 end_address);
+ VAddr AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool,
+ Direction dir = Direction::FromFront);
+ ResultCode Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
+ Direction dir = Direction::FromFront);
+ ResultCode Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
+ Direction dir = Direction::FromFront);
+
+ static constexpr std::size_t MaxManagerCount = 10;
+
+private:
+ class Impl final : NonCopyable {
+ private:
+ using RefCount = u16;
+
+ private:
+ PageHeap heap;
+ Pool pool{};
+
+ public:
+ Impl() = default;
+
+ std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
+
+ VAddr AllocateBlock(s32 index) {
+ return heap.AllocateBlock(index);
+ }
+
+ void Free(VAddr addr, std::size_t num_pages) {
+ heap.Free(addr, num_pages);
+ }
+
+ constexpr std::size_t GetSize() const {
+ return heap.GetSize();
+ }
+
+ constexpr VAddr GetAddress() const {
+ return heap.GetAddress();
+ }
+
+ constexpr VAddr GetEndAddress() const {
+ return heap.GetEndAddress();
+ }
+ };
+
+private:
+ std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
+ std::array<Impl, MaxManagerCount> managers;
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_types.h b/src/core/hle/kernel/memory/memory_types.h
new file mode 100644
index 000000000..a75bf77c0
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_types.h
@@ -0,0 +1,18 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Kernel::Memory {
+
+constexpr std::size_t PageBits{12};
+constexpr std::size_t PageSize{1 << PageBits};
+
+using Page = std::array<u8, PageSize>;
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/page_heap.cpp b/src/core/hle/kernel/memory/page_heap.cpp
new file mode 100644
index 000000000..0ab1f7205
--- /dev/null
+++ b/src/core/hle/kernel/memory/page_heap.cpp
@@ -0,0 +1,119 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// This file references various implementation details from Atmosphere, an open-source firmware for
+// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+
+#include "core/core.h"
+#include "core/hle/kernel/memory/page_heap.h"
+#include "core/memory.h"
+
+namespace Kernel::Memory {
+
+void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
+ // Check our assumptions
+ ASSERT(Common::IsAligned((address), PageSize));
+ ASSERT(Common::IsAligned(size, PageSize));
+
+ // Set our members
+ heap_address = address;
+ heap_size = size;
+
+ // Setup bitmaps
+ metadata.resize(metadata_size / sizeof(u64));
+ u64* cur_bitmap_storage{metadata.data()};
+ for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
+ const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
+ const std::size_t next_block_shift{
+ (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
+ cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift,
+ next_block_shift, cur_bitmap_storage);
+ }
+}
+
+VAddr PageHeap::AllocateBlock(s32 index) {
+ const std::size_t needed_size{blocks[index].GetSize()};
+
+ for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) {
+ if (const VAddr addr{blocks[i].PopBlock()}; addr) {
+ if (const std::size_t allocated_size{blocks[i].GetSize()};
+ allocated_size > needed_size) {
+ Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
+ }
+ return addr;
+ }
+ }
+
+ return 0;
+}
+
+void PageHeap::FreeBlock(VAddr block, s32 index) {
+ do {
+ block = blocks[index++].PushBlock(block);
+ } while (block != 0);
+}
+
+void PageHeap::Free(VAddr addr, std::size_t num_pages) {
+ // Freeing no pages is a no-op
+ if (num_pages == 0) {
+ return;
+ }
+
+ // Find the largest block size that we can free, and free as many as possible
+ s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1};
+ const VAddr start{addr};
+ const VAddr end{(num_pages * PageSize) + addr};
+ VAddr before_start{start};
+ VAddr before_end{start};
+ VAddr after_start{end};
+ VAddr after_end{end};
+ while (big_index >= 0) {
+ const std::size_t block_size{blocks[big_index].GetSize()};
+ const VAddr big_start{Common::AlignUp((start), block_size)};
+ const VAddr big_end{Common::AlignDown((end), block_size)};
+ if (big_start < big_end) {
+ // Free as many big blocks as we can
+ for (auto block{big_start}; block < big_end; block += block_size) {
+ FreeBlock(block, big_index);
+ }
+ before_end = big_start;
+ after_start = big_end;
+ break;
+ }
+ big_index--;
+ }
+ ASSERT(big_index >= 0);
+
+ // Free space before the big blocks
+ for (s32 i{big_index - 1}; i >= 0; i--) {
+ const std::size_t block_size{blocks[i].GetSize()};
+ while (before_start + block_size <= before_end) {
+ before_end -= block_size;
+ FreeBlock(before_end, i);
+ }
+ }
+
+ // Free space after the big blocks
+ for (s32 i{big_index - 1}; i >= 0; i--) {
+ const std::size_t block_size{blocks[i].GetSize()};
+ while (after_start + block_size <= after_end) {
+ FreeBlock(after_start, i);
+ after_start += block_size;
+ }
+ }
+}
+
+std::size_t PageHeap::CalculateMetadataOverheadSize(std::size_t region_size) {
+ std::size_t overhead_size = 0;
+ for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
+ const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
+ const std::size_t next_block_shift{
+ (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
+ overhead_size += PageHeap::Block::CalculateMetadataOverheadSize(
+ region_size, cur_block_shift, next_block_shift);
+ }
+ return Common::AlignUp(overhead_size, PageSize);
+}
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/page_heap.h b/src/core/hle/kernel/memory/page_heap.h
new file mode 100644
index 000000000..22b0de860
--- /dev/null
+++ b/src/core/hle/kernel/memory/page_heap.h
@@ -0,0 +1,370 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// This file references various implementation details from Atmosphere, an open-source firmware for
+// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/bit_util.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/memory/memory_types.h"
+
+namespace Kernel::Memory {
+
+class PageHeap final : NonCopyable {
+public:
+ static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
+ const auto target_pages{std::max(num_pages, align_pages)};
+ for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
+ if (target_pages <=
+ (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
+ return static_cast<s32>(i);
+ }
+ }
+ return -1;
+ }
+
+ static constexpr s32 GetBlockIndex(std::size_t num_pages) {
+ for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
+ if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ static constexpr std::size_t GetBlockSize(std::size_t index) {
+ return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
+ }
+
+ static constexpr std::size_t GetBlockNumPages(std::size_t index) {
+ return GetBlockSize(index) / PageSize;
+ }
+
+private:
+ static constexpr std::size_t NumMemoryBlockPageShifts{7};
+ static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
+ 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
+ };
+
+ class Block final : NonCopyable {
+ private:
+ class Bitmap final : NonCopyable {
+ public:
+ static constexpr std::size_t MaxDepth{4};
+
+ private:
+ std::array<u64*, MaxDepth> bit_storages{};
+ std::size_t num_bits{};
+ std::size_t used_depths{};
+
+ public:
+ constexpr Bitmap() = default;
+
+ constexpr std::size_t GetNumBits() const {
+ return num_bits;
+ }
+ constexpr s32 GetHighestDepthIndex() const {
+ return static_cast<s32>(used_depths) - 1;
+ }
+
+ constexpr u64* Initialize(u64* storage, std::size_t size) {
+ //* Initially, everything is un-set
+ num_bits = 0;
+
+ // Calculate the needed bitmap depth
+ used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
+ ASSERT(used_depths <= MaxDepth);
+
+ // Set the bitmap pointers
+ for (s32 depth{GetHighestDepthIndex()}; depth >= 0; depth--) {
+ bit_storages[depth] = storage;
+ size = Common::AlignUp(size, 64) / 64;
+ storage += size;
+ }
+
+ return storage;
+ }
+
+ s64 FindFreeBlock() const {
+ uintptr_t offset{};
+ s32 depth{};
+
+ do {
+ const u64 v{bit_storages[depth][offset]};
+ if (v == 0) {
+ // Non-zero depth indicates that a previous level had a free block
+ ASSERT(depth == 0);
+ return -1;
+ }
+ offset = offset * 64 + Common::CountTrailingZeroes64(v);
+ ++depth;
+ } while (depth < static_cast<s32>(used_depths));
+
+ return static_cast<s64>(offset);
+ }
+
+ constexpr void SetBit(std::size_t offset) {
+ SetBit(GetHighestDepthIndex(), offset);
+ num_bits++;
+ }
+
+ constexpr void ClearBit(std::size_t offset) {
+ ClearBit(GetHighestDepthIndex(), offset);
+ num_bits--;
+ }
+
+ constexpr bool ClearRange(std::size_t offset, std::size_t count) {
+ const s32 depth{GetHighestDepthIndex()};
+ const auto bit_ind{offset / 64};
+ u64* bits{bit_storages[depth]};
+ if (count < 64) {
+ const auto shift{offset % 64};
+ ASSERT(shift + count <= 64);
+ // Check that all the bits are set
+ const u64 mask{((1ULL << count) - 1) << shift};
+ u64 v{bits[bit_ind]};
+ if ((v & mask) != mask) {
+ return false;
+ }
+
+ // Clear the bits
+ v &= ~mask;
+ bits[bit_ind] = v;
+ if (v == 0) {
+ ClearBit(depth - 1, bit_ind);
+ }
+ } else {
+ ASSERT(offset % 64 == 0);
+ ASSERT(count % 64 == 0);
+ // Check that all the bits are set
+ std::size_t remaining{count};
+ std::size_t i = 0;
+ do {
+ if (bits[bit_ind + i++] != ~u64(0)) {
+ return false;
+ }
+ remaining -= 64;
+ } while (remaining > 0);
+
+ // Clear the bits
+ remaining = count;
+ i = 0;
+ do {
+ bits[bit_ind + i] = 0;
+ ClearBit(depth - 1, bit_ind + i);
+ i++;
+ remaining -= 64;
+ } while (remaining > 0);
+ }
+
+ num_bits -= count;
+ return true;
+ }
+
+ private:
+ constexpr void SetBit(s32 depth, std::size_t offset) {
+ while (depth >= 0) {
+ const auto ind{offset / 64};
+ const auto which{offset % 64};
+ const u64 mask{1ULL << which};
+
+ u64* bit{std::addressof(bit_storages[depth][ind])};
+ const u64 v{*bit};
+ ASSERT((v & mask) == 0);
+ *bit = v | mask;
+ if (v) {
+ break;
+ }
+ offset = ind;
+ depth--;
+ }
+ }
+
+ constexpr void ClearBit(s32 depth, std::size_t offset) {
+ while (depth >= 0) {
+ const auto ind{offset / 64};
+ const auto which{offset % 64};
+ const u64 mask{1ULL << which};
+
+ u64* bit{std::addressof(bit_storages[depth][ind])};
+ u64 v{*bit};
+ ASSERT((v & mask) != 0);
+ v &= ~mask;
+ *bit = v;
+ if (v) {
+ break;
+ }
+ offset = ind;
+ depth--;
+ }
+ }
+
+ private:
+ static constexpr s32 GetRequiredDepth(std::size_t region_size) {
+ s32 depth = 0;
+ while (true) {
+ region_size /= 64;
+ depth++;
+ if (region_size == 0) {
+ return depth;
+ }
+ }
+ }
+
+ public:
+ static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size) {
+ std::size_t overhead_bits = 0;
+ for (s32 depth{GetRequiredDepth(region_size) - 1}; depth >= 0; depth--) {
+ region_size = Common::AlignUp(region_size, 64) / 64;
+ overhead_bits += region_size;
+ }
+ return overhead_bits * sizeof(u64);
+ }
+ };
+
+ private:
+ Bitmap bitmap;
+ VAddr heap_address{};
+ uintptr_t end_offset{};
+ std::size_t block_shift{};
+ std::size_t next_block_shift{};
+
+ public:
+ constexpr Block() = default;
+
+ constexpr std::size_t GetShift() const {
+ return block_shift;
+ }
+ constexpr std::size_t GetNextShift() const {
+ return next_block_shift;
+ }
+ constexpr std::size_t GetSize() const {
+ return static_cast<std::size_t>(1) << GetShift();
+ }
+ constexpr std::size_t GetNumPages() const {
+ return GetSize() / PageSize;
+ }
+ constexpr std::size_t GetNumFreeBlocks() const {
+ return bitmap.GetNumBits();
+ }
+ constexpr std::size_t GetNumFreePages() const {
+ return GetNumFreeBlocks() * GetNumPages();
+ }
+
+ constexpr u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
+ u64* bit_storage) {
+ // Set shifts
+ block_shift = bs;
+ next_block_shift = nbs;
+
+ // Align up the address
+ VAddr end{addr + size};
+ const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
+ : (1ULL << block_shift)};
+ addr = Common::AlignDown((addr), align);
+ end = Common::AlignUp((end), align);
+
+ heap_address = addr;
+ end_offset = (end - addr) / (1ULL << block_shift);
+ return bitmap.Initialize(bit_storage, end_offset);
+ }
+
+ constexpr VAddr PushBlock(VAddr address) {
+ // Set the bit for the free block
+ std::size_t offset{(address - heap_address) >> GetShift()};
+ bitmap.SetBit(offset);
+
+ // If we have a next shift, try to clear the blocks below and return the address
+ if (GetNextShift()) {
+ const auto diff{1ULL << (GetNextShift() - GetShift())};
+ offset = Common::AlignDown(offset, diff);
+ if (bitmap.ClearRange(offset, diff)) {
+ return heap_address + (offset << GetShift());
+ }
+ }
+
+ // We couldn't coalesce, or we're already as big as possible
+ return 0;
+ }
+
+ VAddr PopBlock() {
+ // Find a free block
+ const s64 soffset{bitmap.FindFreeBlock()};
+ if (soffset < 0) {
+ return 0;
+ }
+ const auto offset{static_cast<std::size_t>(soffset)};
+
+ // Update our tracking and return it
+ bitmap.ClearBit(offset);
+ return heap_address + (offset << GetShift());
+ }
+
+ public:
+ static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size,
+ std::size_t cur_block_shift,
+ std::size_t next_block_shift) {
+ const auto cur_block_size{(1ULL << cur_block_shift)};
+ const auto next_block_size{(1ULL << next_block_shift)};
+ const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
+ return Bitmap::CalculateMetadataOverheadSize(
+ (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
+ }
+ };
+
+public:
+ PageHeap() = default;
+
+ constexpr VAddr GetAddress() const {
+ return heap_address;
+ }
+ constexpr std::size_t GetSize() const {
+ return heap_size;
+ }
+ constexpr VAddr GetEndAddress() const {
+ return GetAddress() + GetSize();
+ }
+ constexpr std::size_t GetPageOffset(VAddr block) const {
+ return (block - GetAddress()) / PageSize;
+ }
+
+ void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
+ VAddr AllocateBlock(s32 index);
+ void Free(VAddr addr, std::size_t num_pages);
+
+ void UpdateUsedSize() {
+ used_size = heap_size - (GetNumFreePages() * PageSize);
+ }
+
+ static std::size_t CalculateMetadataOverheadSize(std::size_t region_size);
+
+private:
+ constexpr std::size_t GetNumFreePages() const {
+ std::size_t num_free{};
+
+ for (const auto& block : blocks) {
+ num_free += block.GetNumFreePages();
+ }
+
+ return num_free;
+ }
+
+ void FreeBlock(VAddr block, s32 index);
+
+ VAddr heap_address{};
+ std::size_t heap_size{};
+ std::size_t used_size{};
+ std::array<Block, NumMemoryBlockPageShifts> blocks{};
+ std::vector<u64> metadata;
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/page_linked_list.h b/src/core/hle/kernel/memory/page_linked_list.h
new file mode 100644
index 000000000..45dc13eaf
--- /dev/null
+++ b/src/core/hle/kernel/memory/page_linked_list.h
@@ -0,0 +1,92 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <list>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/memory/memory_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel::Memory {
+
+class PageLinkedList final {
+public:
+ class Node final {
+ public:
+ constexpr Node(u64 addr, std::size_t num_pages) : addr{addr}, num_pages{num_pages} {}
+
+ constexpr u64 GetAddress() const {
+ return addr;
+ }
+
+ constexpr std::size_t GetNumPages() const {
+ return num_pages;
+ }
+
+ private:
+ u64 addr{};
+ std::size_t num_pages{};
+ };
+
+public:
+ PageLinkedList() = default;
+ PageLinkedList(u64 address, u64 num_pages) {
+ ASSERT(AddBlock(address, num_pages).IsSuccess());
+ }
+
+ constexpr std::list<Node>& Nodes() {
+ return nodes;
+ }
+
+ constexpr const std::list<Node>& Nodes() const {
+ return nodes;
+ }
+
+ std::size_t GetNumPages() const {
+ std::size_t num_pages = 0;
+ for (const Node& node : nodes) {
+ num_pages += node.GetNumPages();
+ }
+ return num_pages;
+ }
+
+ bool IsEqual(PageLinkedList& other) const {
+ auto this_node = nodes.begin();
+ auto other_node = other.nodes.begin();
+ while (this_node != nodes.end() && other_node != other.nodes.end()) {
+ if (this_node->GetAddress() != other_node->GetAddress() ||
+ this_node->GetNumPages() != other_node->GetNumPages()) {
+ return false;
+ }
+ this_node = std::next(this_node);
+ other_node = std::next(other_node);
+ }
+
+ return this_node == nodes.end() && other_node == other.nodes.end();
+ }
+
+ ResultCode AddBlock(u64 address, u64 num_pages) {
+ if (!num_pages) {
+ return RESULT_SUCCESS;
+ }
+ if (!nodes.empty()) {
+ const auto node = nodes.back();
+ if (node.GetAddress() + node.GetNumPages() * PageSize == address) {
+ address = node.GetAddress();
+ num_pages += node.GetNumPages();
+ nodes.pop_back();
+ }
+ }
+ nodes.push_back({address, num_pages});
+ return RESULT_SUCCESS;
+ }
+
+private:
+ std::list<Node> nodes;
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
new file mode 100644
index 000000000..a3fadb533
--- /dev/null
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -0,0 +1,1174 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory/address_space_info.h"
+#include "core/hle/kernel/memory/memory_block.h"
+#include "core/hle/kernel/memory/memory_block_manager.h"
+#include "core/hle/kernel/memory/page_linked_list.h"
+#include "core/hle/kernel/memory/page_table.h"
+#include "core/hle/kernel/memory/system_control.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/resource_limit.h"
+#include "core/memory.h"
+
+namespace Kernel::Memory {
+
+namespace {
+
+constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
+ switch (as_type) {
+ case FileSys::ProgramAddressSpaceType::Is32Bit:
+ case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
+ return 32;
+ case FileSys::ProgramAddressSpaceType::Is36Bit:
+ return 36;
+ case FileSys::ProgramAddressSpaceType::Is39Bit:
+ return 39;
+ default:
+ UNREACHABLE();
+ return {};
+ }
+}
+
+constexpr u64 GetAddressInRange(const MemoryInfo& info, VAddr addr) {
+ if (info.GetAddress() < addr) {
+ return addr;
+ }
+ return info.GetAddress();
+}
+
+constexpr std::size_t GetSizeInRange(const MemoryInfo& info, VAddr start, VAddr end) {
+ std::size_t size{info.GetSize()};
+ if (info.GetAddress() < start) {
+ size -= start - info.GetAddress();
+ }
+ if (info.GetEndAddress() > end) {
+ size -= info.GetEndAddress() - end;
+ }
+ return size;
+}
+
+} // namespace
+
+PageTable::PageTable(Core::System& system) : system{system} {}
+
+ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
+ bool enable_aslr, VAddr code_addr, std::size_t code_size,
+ Memory::MemoryManager::Pool pool) {
+
+ const auto GetSpaceStart = [this](AddressSpaceInfo::Type type) {
+ return AddressSpaceInfo::GetAddressSpaceStart(address_space_width, type);
+ };
+ const auto GetSpaceSize = [this](AddressSpaceInfo::Type type) {
+ return AddressSpaceInfo::GetAddressSpaceSize(address_space_width, type);
+ };
+
+ // Set our width and heap/alias sizes
+ address_space_width = GetAddressSpaceWidthFromType(as_type);
+ const VAddr start = 0;
+ const VAddr end{1ULL << address_space_width};
+ std::size_t alias_region_size{GetSpaceSize(AddressSpaceInfo::Type::Alias)};
+ std::size_t heap_region_size{GetSpaceSize(AddressSpaceInfo::Type::Heap)};
+
+ ASSERT(start <= code_addr);
+ ASSERT(code_addr < code_addr + code_size);
+ ASSERT(code_addr + code_size - 1 <= end - 1);
+
+ // Adjust heap/alias size if we don't have an alias region
+ if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
+ heap_region_size += alias_region_size;
+ alias_region_size = 0;
+ }
+
+ // Set code regions and determine remaining
+ constexpr std::size_t RegionAlignment{2 * 1024 * 1024};
+ VAddr process_code_start{};
+ VAddr process_code_end{};
+ std::size_t stack_region_size{};
+ std::size_t kernel_map_region_size{};
+
+ if (address_space_width == 39) {
+ alias_region_size = GetSpaceSize(AddressSpaceInfo::Type::Alias);
+ heap_region_size = GetSpaceSize(AddressSpaceInfo::Type::Heap);
+ stack_region_size = GetSpaceSize(AddressSpaceInfo::Type::Stack);
+ kernel_map_region_size = GetSpaceSize(AddressSpaceInfo::Type::Is32Bit);
+ code_region_start = GetSpaceStart(AddressSpaceInfo::Type::Large64Bit);
+ code_region_end = code_region_start + GetSpaceSize(AddressSpaceInfo::Type::Large64Bit);
+ alias_code_region_start = code_region_start;
+ alias_code_region_end = code_region_end;
+ process_code_start = Common::AlignDown(code_addr, RegionAlignment);
+ process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment);
+ } else {
+ stack_region_size = 0;
+ kernel_map_region_size = 0;
+ code_region_start = GetSpaceStart(AddressSpaceInfo::Type::Is32Bit);
+ code_region_end = code_region_start + GetSpaceSize(AddressSpaceInfo::Type::Is32Bit);
+ stack_region_start = code_region_start;
+ alias_code_region_start = code_region_start;
+ alias_code_region_end = GetSpaceStart(AddressSpaceInfo::Type::Small64Bit) +
+ GetSpaceSize(AddressSpaceInfo::Type::Small64Bit);
+ stack_region_end = code_region_end;
+ kernel_map_region_start = code_region_start;
+ kernel_map_region_end = code_region_end;
+ process_code_start = code_region_start;
+ process_code_end = code_region_end;
+ }
+
+ // Set other basic fields
+ is_aslr_enabled = enable_aslr;
+ address_space_start = start;
+ address_space_end = end;
+ is_kernel = false;
+
+ // Determine the region we can place our undetermineds in
+ VAddr alloc_start{};
+ std::size_t alloc_size{};
+ if ((process_code_start - code_region_start) >= (end - process_code_end)) {
+ alloc_start = code_region_start;
+ alloc_size = process_code_start - code_region_start;
+ } else {
+ alloc_start = process_code_end;
+ alloc_size = end - process_code_end;
+ }
+ const std::size_t needed_size{
+ (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
+ if (alloc_size < needed_size) {
+ UNREACHABLE();
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ const std::size_t remaining_size{alloc_size - needed_size};
+
+ // Determine random placements for each region
+ std::size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{};
+ if (enable_aslr) {
+ alias_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
+ RegionAlignment;
+ heap_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
+ RegionAlignment;
+ stack_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
+ RegionAlignment;
+ kmap_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
+ RegionAlignment;
+ }
+
+ // Setup heap and alias regions
+ alias_region_start = alloc_start + alias_rnd;
+ alias_region_end = alias_region_start + alias_region_size;
+ heap_region_start = alloc_start + heap_rnd;
+ heap_region_end = heap_region_start + heap_region_size;
+
+ if (alias_rnd <= heap_rnd) {
+ heap_region_start += alias_region_size;
+ heap_region_end += alias_region_size;
+ } else {
+ alias_region_start += heap_region_size;
+ alias_region_end += heap_region_size;
+ }
+
+ // Setup stack region
+ if (stack_region_size) {
+ stack_region_start = alloc_start + stack_rnd;
+ stack_region_end = stack_region_start + stack_region_size;
+
+ if (alias_rnd < stack_rnd) {
+ stack_region_start += alias_region_size;
+ stack_region_end += alias_region_size;
+ } else {
+ alias_region_start += stack_region_size;
+ alias_region_end += stack_region_size;
+ }
+
+ if (heap_rnd < stack_rnd) {
+ stack_region_start += heap_region_size;
+ stack_region_end += heap_region_size;
+ } else {
+ heap_region_start += stack_region_size;
+ heap_region_end += stack_region_size;
+ }
+ }
+
+ // Setup kernel map region
+ if (kernel_map_region_size) {
+ kernel_map_region_start = alloc_start + kmap_rnd;
+ kernel_map_region_end = kernel_map_region_start + kernel_map_region_size;
+
+ if (alias_rnd < kmap_rnd) {
+ kernel_map_region_start += alias_region_size;
+ kernel_map_region_end += alias_region_size;
+ } else {
+ alias_region_start += kernel_map_region_size;
+ alias_region_end += kernel_map_region_size;
+ }
+
+ if (heap_rnd < kmap_rnd) {
+ kernel_map_region_start += heap_region_size;
+ kernel_map_region_end += heap_region_size;
+ } else {
+ heap_region_start += kernel_map_region_size;
+ heap_region_end += kernel_map_region_size;
+ }
+
+ if (stack_region_size) {
+ if (stack_rnd < kmap_rnd) {
+ kernel_map_region_start += stack_region_size;
+ kernel_map_region_end += stack_region_size;
+ } else {
+ stack_region_start += kernel_map_region_size;
+ stack_region_end += kernel_map_region_size;
+ }
+ }
+ }
+
+ // Set heap members
+ current_heap_end = heap_region_start;
+ max_heap_size = 0;
+ max_physical_memory_size = 0;
+
+ // Ensure that we regions inside our address space
+ auto IsInAddressSpace = [&](VAddr addr) {
+ return address_space_start <= addr && addr <= address_space_end;
+ };
+ ASSERT(IsInAddressSpace(alias_region_start));
+ ASSERT(IsInAddressSpace(alias_region_end));
+ ASSERT(IsInAddressSpace(heap_region_start));
+ ASSERT(IsInAddressSpace(heap_region_end));
+ ASSERT(IsInAddressSpace(stack_region_start));
+ ASSERT(IsInAddressSpace(stack_region_end));
+ ASSERT(IsInAddressSpace(kernel_map_region_start));
+ ASSERT(IsInAddressSpace(kernel_map_region_end));
+
+ // Ensure that we selected regions that don't overlap
+ const VAddr alias_start{alias_region_start};
+ const VAddr alias_last{alias_region_end - 1};
+ const VAddr heap_start{heap_region_start};
+ const VAddr heap_last{heap_region_end - 1};
+ const VAddr stack_start{stack_region_start};
+ const VAddr stack_last{stack_region_end - 1};
+ const VAddr kmap_start{kernel_map_region_start};
+ const VAddr kmap_last{kernel_map_region_end - 1};
+ ASSERT(alias_last < heap_start || heap_last < alias_start);
+ ASSERT(alias_last < stack_start || stack_last < alias_start);
+ ASSERT(alias_last < kmap_start || kmap_last < alias_start);
+ ASSERT(heap_last < stack_start || stack_last < heap_start);
+ ASSERT(heap_last < kmap_start || kmap_last < heap_start);
+
+ current_heap_addr = heap_region_start;
+ heap_capacity = 0;
+ physical_memory_usage = 0;
+ memory_pool = pool;
+
+ page_table_impl.Resize(address_space_width, PageBits, true);
+
+ return InitializeMemoryLayout(start, end);
+}
+
+ResultCode PageTable::MapProcessCode(VAddr addr, std::size_t num_pages, MemoryState state,
+ MemoryPermission perm) {
+ std::lock_guard lock{page_table_lock};
+
+ const u64 size{num_pages * PageSize};
+
+ if (!CanContain(addr, size, state)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (IsRegionMapped(addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ PageLinkedList page_linked_list;
+ CASCADE_CODE(
+ system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
+ CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
+
+ block_manager->Update(addr, num_pages, state, perm);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ const std::size_t num_pages{size / PageSize};
+
+ MemoryState state{};
+ MemoryPermission perm{};
+ CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, MemoryState::All,
+ MemoryState::Normal, MemoryPermission::Mask,
+ MemoryPermission::ReadAndWrite, MemoryAttribute::Mask,
+ MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
+
+ if (IsRegionMapped(dst_addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ PageLinkedList page_linked_list;
+ AddRegionToPages(src_addr, num_pages, page_linked_list);
+
+ {
+ auto block_guard = detail::ScopeExit(
+ [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); });
+
+ CASCADE_CODE(
+ Operate(src_addr, num_pages, MemoryPermission::None, OperationType::ChangePermissions));
+ CASCADE_CODE(MapPages(dst_addr, page_linked_list, MemoryPermission::None));
+
+ block_guard.Cancel();
+ }
+
+ block_manager->Update(src_addr, num_pages, state, MemoryPermission::None,
+ MemoryAttribute::Locked);
+ block_manager->Update(dst_addr, num_pages, MemoryState::AliasCode);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ if (!size) {
+ return RESULT_SUCCESS;
+ }
+
+ const std::size_t num_pages{size / PageSize};
+
+ CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, src_addr, size, MemoryState::All,
+ MemoryState::Normal, MemoryPermission::None,
+ MemoryPermission::None, MemoryAttribute::Mask,
+ MemoryAttribute::Locked, MemoryAttribute::IpcAndDeviceMapped));
+
+ MemoryState state{};
+ CASCADE_CODE(CheckMemoryState(
+ &state, nullptr, nullptr, dst_addr, PageSize, MemoryState::FlagCanCodeAlias,
+ MemoryState::FlagCanCodeAlias, MemoryPermission::None, MemoryPermission::None,
+ MemoryAttribute::Mask, MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
+ CASCADE_CODE(CheckMemoryState(dst_addr, size, MemoryState::All, state, MemoryPermission::None,
+ MemoryPermission::None, MemoryAttribute::Mask,
+ MemoryAttribute::None));
+ CASCADE_CODE(Operate(dst_addr, num_pages, MemoryPermission::None, OperationType::Unmap));
+
+ block_manager->Update(dst_addr, num_pages, MemoryState::Free);
+ block_manager->Update(src_addr, num_pages, MemoryState::Normal, MemoryPermission::ReadAndWrite);
+
+ return RESULT_SUCCESS;
+}
+
+void PageTable::MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end) {
+ auto node{page_linked_list.Nodes().begin()};
+ PAddr map_addr{node->GetAddress()};
+ std::size_t src_num_pages{node->GetNumPages()};
+
+ block_manager->IterateForRange(start, end, [&](const MemoryInfo& info) {
+ if (info.state != MemoryState::Free) {
+ return;
+ }
+
+ std::size_t dst_num_pages{GetSizeInRange(info, start, end) / PageSize};
+ VAddr dst_addr{GetAddressInRange(info, start)};
+
+ while (dst_num_pages) {
+ if (!src_num_pages) {
+ node = std::next(node);
+ map_addr = node->GetAddress();
+ src_num_pages = node->GetNumPages();
+ }
+
+ const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
+ Operate(dst_addr, num_pages, MemoryPermission::ReadAndWrite, OperationType::Map,
+ map_addr);
+
+ dst_addr += num_pages * PageSize;
+ map_addr += num_pages * PageSize;
+ src_num_pages -= num_pages;
+ dst_num_pages -= num_pages;
+ }
+ });
+}
+
+ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ std::size_t mapped_size{};
+ const VAddr end_addr{addr + size};
+
+ block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) {
+ if (info.state != MemoryState::Free) {
+ mapped_size += GetSizeInRange(info, addr, end_addr);
+ }
+ });
+
+ if (mapped_size == size) {
+ return RESULT_SUCCESS;
+ }
+
+ auto process{system.Kernel().CurrentProcess()};
+ const std::size_t remaining_size{size - mapped_size};
+ const std::size_t remaining_pages{remaining_size / PageSize};
+
+ if (process->GetResourceLimit() &&
+ !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, remaining_size)) {
+ return ERR_RESOURCE_LIMIT_EXCEEDED;
+ }
+
+ PageLinkedList page_linked_list;
+ {
+ auto block_guard = detail::ScopeExit([&] {
+ system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool);
+ process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, remaining_size);
+ });
+
+ CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
+ memory_pool));
+
+ block_guard.Cancel();
+ }
+
+ MapPhysicalMemory(page_linked_list, addr, end_addr);
+
+ physical_memory_usage += remaining_size;
+
+ const std::size_t num_pages{size / PageSize};
+ block_manager->Update(addr, num_pages, MemoryState::Free, MemoryPermission::None,
+ MemoryAttribute::None, MemoryState::Normal,
+ MemoryPermission::ReadAndWrite, MemoryAttribute::None);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ const VAddr end_addr{addr + size};
+ ResultCode result{RESULT_SUCCESS};
+ std::size_t mapped_size{};
+
+ // Verify that the region can be unmapped
+ block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) {
+ if (info.state == MemoryState::Normal) {
+ if (info.attribute != MemoryAttribute::None) {
+ result = ERR_INVALID_ADDRESS_STATE;
+ return;
+ }
+ mapped_size += GetSizeInRange(info, addr, end_addr);
+ } else if (info.state != MemoryState::Free) {
+ result = ERR_INVALID_ADDRESS_STATE;
+ }
+ });
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ if (!mapped_size) {
+ return RESULT_SUCCESS;
+ }
+
+ CASCADE_CODE(UnmapMemory(addr, size));
+
+ auto process{system.Kernel().CurrentProcess()};
+ process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, mapped_size);
+ physical_memory_usage -= mapped_size;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::UnmapMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ const VAddr end_addr{addr + size};
+ ResultCode result{RESULT_SUCCESS};
+ PageLinkedList page_linked_list;
+
+ // Unmap each region within the range
+ block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) {
+ if (info.state == MemoryState::Normal) {
+ const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
+ const std::size_t block_num_pages{block_size / PageSize};
+ const VAddr block_addr{GetAddressInRange(info, addr)};
+
+ AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
+
+ if (result = Operate(block_addr, block_num_pages, MemoryPermission::None,
+ OperationType::Unmap);
+ result.IsError()) {
+ return;
+ }
+ }
+ });
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ const std::size_t num_pages{size / PageSize};
+ system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool);
+
+ block_manager->Update(addr, num_pages, MemoryState::Free);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryState src_state{};
+ CASCADE_CODE(CheckMemoryState(
+ &src_state, nullptr, nullptr, src_addr, size, MemoryState::FlagCanAlias,
+ MemoryState::FlagCanAlias, MemoryPermission::Mask, MemoryPermission::ReadAndWrite,
+ MemoryAttribute::Mask, MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
+
+ if (IsRegionMapped(dst_addr, size)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ PageLinkedList page_linked_list;
+ const std::size_t num_pages{size / PageSize};
+
+ AddRegionToPages(src_addr, num_pages, page_linked_list);
+
+ {
+ auto block_guard = detail::ScopeExit([&] {
+ Operate(src_addr, num_pages, MemoryPermission::ReadAndWrite,
+ OperationType::ChangePermissions);
+ });
+
+ CASCADE_CODE(
+ Operate(src_addr, num_pages, MemoryPermission::None, OperationType::ChangePermissions));
+ CASCADE_CODE(MapPages(dst_addr, page_linked_list, MemoryPermission::ReadAndWrite));
+
+ block_guard.Cancel();
+ }
+
+ block_manager->Update(src_addr, num_pages, src_state, MemoryPermission::None,
+ MemoryAttribute::Locked);
+ block_manager->Update(dst_addr, num_pages, MemoryState::Stack, MemoryPermission::ReadAndWrite);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryState src_state{};
+ CASCADE_CODE(CheckMemoryState(
+ &src_state, nullptr, nullptr, src_addr, size, MemoryState::FlagCanAlias,
+ MemoryState::FlagCanAlias, MemoryPermission::Mask, MemoryPermission::None,
+ MemoryAttribute::Mask, MemoryAttribute::Locked, MemoryAttribute::IpcAndDeviceMapped));
+
+ MemoryPermission dst_perm{};
+ CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, dst_addr, size, MemoryState::All,
+ MemoryState::Stack, MemoryPermission::None,
+ MemoryPermission::None, MemoryAttribute::Mask,
+ MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
+
+ PageLinkedList src_pages;
+ PageLinkedList dst_pages;
+ const std::size_t num_pages{size / PageSize};
+
+ AddRegionToPages(src_addr, num_pages, src_pages);
+ AddRegionToPages(dst_addr, num_pages, dst_pages);
+
+ if (!dst_pages.IsEqual(src_pages)) {
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ {
+ auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); });
+
+ CASCADE_CODE(Operate(dst_addr, num_pages, MemoryPermission::None, OperationType::Unmap));
+ CASCADE_CODE(Operate(src_addr, num_pages, MemoryPermission::ReadAndWrite,
+ OperationType::ChangePermissions));
+
+ block_guard.Cancel();
+ }
+
+ block_manager->Update(src_addr, num_pages, src_state, MemoryPermission::ReadAndWrite);
+ block_manager->Update(dst_addr, num_pages, MemoryState::Free);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_list,
+ MemoryPermission perm) {
+ VAddr cur_addr{addr};
+
+ for (const auto& node : page_linked_list.Nodes()) {
+ if (const auto result{
+ Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
+ result.IsError()) {
+ const std::size_t num_pages{(addr - cur_addr) / PageSize};
+
+ ASSERT(
+ Operate(addr, num_pages, MemoryPermission::None, OperationType::Unmap).IsSuccess());
+
+ return result;
+ }
+
+ cur_addr += node.GetNumPages() * PageSize;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state,
+ MemoryPermission perm) {
+ std::lock_guard lock{page_table_lock};
+
+ const std::size_t num_pages{page_linked_list.GetNumPages()};
+ const std::size_t size{num_pages * PageSize};
+
+ if (!CanContain(addr, size, state)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (IsRegionMapped(addr, num_pages * PageSize)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ CASCADE_CODE(MapPages(addr, page_linked_list, perm));
+
+ block_manager->Update(addr, num_pages, state, perm);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm) {
+
+ std::lock_guard lock{page_table_lock};
+
+ MemoryState prev_state{};
+ MemoryPermission prev_perm{};
+
+ CASCADE_CODE(CheckMemoryState(
+ &prev_state, &prev_perm, nullptr, addr, size, MemoryState::FlagCode, MemoryState::FlagCode,
+ MemoryPermission::None, MemoryPermission::None, MemoryAttribute::Mask,
+ MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
+
+ MemoryState state{prev_state};
+
+ // Ensure state is mutable if permission allows write
+ if ((perm & MemoryPermission::Write) != MemoryPermission::None) {
+ if (prev_state == MemoryState::Code) {
+ state = MemoryState::CodeData;
+ } else if (prev_state == MemoryState::AliasCode) {
+ state = MemoryState::AliasCodeData;
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ // Return early if there is nothing to change
+ if (state == prev_state && perm == prev_perm) {
+ return RESULT_SUCCESS;
+ }
+
+ const std::size_t num_pages{size / PageSize};
+ const OperationType operation{(perm & MemoryPermission::Execute) != MemoryPermission::None
+ ? OperationType::ChangePermissionsAndRefresh
+ : OperationType::ChangePermissions};
+
+ CASCADE_CODE(Operate(addr, num_pages, perm, operation));
+
+ block_manager->Update(addr, num_pages, state, perm);
+
+ return RESULT_SUCCESS;
+}
+
+MemoryInfo PageTable::QueryInfoImpl(VAddr addr) {
+ std::lock_guard lock{page_table_lock};
+
+ return block_manager->FindBlock(addr).GetMemoryInfo();
+}
+
+MemoryInfo PageTable::QueryInfo(VAddr addr) {
+ if (!Contains(addr, 1)) {
+ return {address_space_end, 0 - address_space_end, MemoryState::Inaccessible,
+ MemoryPermission::None, MemoryAttribute::None, MemoryPermission::None};
+ }
+
+ return QueryInfoImpl(addr);
+}
+
+ResultCode PageTable::ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryState state{};
+ MemoryAttribute attribute{};
+
+ CASCADE_CODE(CheckMemoryState(&state, nullptr, &attribute, addr, size,
+ MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted,
+ MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted,
+ MemoryPermission::Mask, MemoryPermission::ReadAndWrite,
+ MemoryAttribute::Mask, MemoryAttribute::None,
+ MemoryAttribute::IpcAndDeviceMapped));
+
+ block_manager->Update(addr, size / PageSize, state, perm, attribute | MemoryAttribute::Locked);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryState state{};
+
+ CASCADE_CODE(CheckMemoryState(&state, nullptr, nullptr, addr, size,
+ MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted,
+ MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted,
+ MemoryPermission::None, MemoryPermission::None,
+ MemoryAttribute::Mask, MemoryAttribute::Locked,
+ MemoryAttribute::IpcAndDeviceMapped));
+
+ block_manager->Update(addr, size / PageSize, state, MemoryPermission::ReadAndWrite);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask,
+ MemoryAttribute value) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryState state{};
+ MemoryPermission perm{};
+ MemoryAttribute attribute{};
+
+ CASCADE_CODE(CheckMemoryState(&state, &perm, &attribute, addr, size,
+ MemoryState::FlagCanChangeAttribute,
+ MemoryState::FlagCanChangeAttribute, MemoryPermission::None,
+ MemoryPermission::None, MemoryAttribute::LockedAndIpcLocked,
+ MemoryAttribute::None, MemoryAttribute::DeviceSharedAndUncached));
+
+ attribute = attribute & ~mask;
+ attribute = attribute | (mask & value);
+
+ block_manager->Update(addr, size / PageSize, state, perm, attribute);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
+ std::lock_guard lock{page_table_lock};
+ heap_capacity = new_heap_capacity;
+ return RESULT_SUCCESS;
+}
+
+ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
+
+ if (size > heap_region_end - heap_region_start) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ const u64 previous_heap_size{GetHeapSize()};
+
+ UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented");
+
+ // Increase the heap size
+ {
+ std::lock_guard lock{page_table_lock};
+
+ const u64 delta{size - previous_heap_size};
+
+ auto process{system.Kernel().CurrentProcess()};
+ if (process->GetResourceLimit() && delta != 0 &&
+ !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, delta)) {
+ return ERR_RESOURCE_LIMIT_EXCEEDED;
+ }
+
+ PageLinkedList page_linked_list;
+ const std::size_t num_pages{delta / PageSize};
+
+ CASCADE_CODE(
+ system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
+
+ if (IsRegionMapped(current_heap_addr, delta)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ CASCADE_CODE(
+ Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
+
+ block_manager->Update(current_heap_addr, num_pages, MemoryState::Normal,
+ MemoryPermission::ReadAndWrite);
+
+ current_heap_addr = heap_region_start + size;
+ }
+
+ return MakeResult<VAddr>(heap_region_start);
+}
+
+ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
+ bool is_map_only, VAddr region_start,
+ std::size_t region_num_pages, MemoryState state,
+ MemoryPermission perm, PAddr map_addr) {
+ std::lock_guard lock{page_table_lock};
+
+ if (!CanContain(region_start, region_num_pages * PageSize, state)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (region_num_pages <= needed_num_pages) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ const VAddr addr{
+ AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
+ if (!addr) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ if (is_map_only) {
+ CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
+ } else {
+ PageLinkedList page_group;
+ CASCADE_CODE(
+ system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool));
+ CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
+ }
+
+ block_manager->Update(addr, needed_num_pages, state, perm);
+
+ return MakeResult<VAddr>(addr);
+}
+
+ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryPermission perm{};
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute,
+ MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None,
+ MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None,
+ MemoryAttribute::DeviceSharedAndUncached)};
+ result.IsError()) {
+ return result;
+ }
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](MemoryBlockManager::iterator block, MemoryPermission perm) {
+ block->ShareToDevice(perm);
+ },
+ perm);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryPermission perm{};
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute,
+ MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None,
+ MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None,
+ MemoryAttribute::DeviceSharedAndUncached)};
+ result.IsError()) {
+ return result;
+ }
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](MemoryBlockManager::iterator block, MemoryPermission perm) {
+ block->UnshareToDevice(perm);
+ },
+ perm);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
+ block_manager = std::make_unique<MemoryBlockManager>(start, end);
+
+ return RESULT_SUCCESS;
+}
+
+bool PageTable::IsRegionMapped(VAddr address, u64 size) {
+ return CheckMemoryState(address, size, MemoryState::All, MemoryState::Free,
+ MemoryPermission::Mask, MemoryPermission::None, MemoryAttribute::Mask,
+ MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped)
+ .IsError();
+}
+
+bool PageTable::IsRegionContiguous(VAddr addr, u64 size) const {
+ auto start_ptr = system.Memory().GetPointer(addr);
+ for (u64 offset{}; offset < size; offset += PageSize) {
+ if (start_ptr != system.Memory().GetPointer(addr + offset)) {
+ return false;
+ }
+ start_ptr += PageSize;
+ }
+ return true;
+}
+
+void PageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
+ PageLinkedList& page_linked_list) {
+ VAddr addr{start};
+ while (addr < start + (num_pages * PageSize)) {
+ const PAddr paddr{GetPhysicalAddr(addr)};
+ if (!paddr) {
+ UNREACHABLE();
+ }
+ page_linked_list.AddBlock(paddr, 1);
+ addr += PageSize;
+ }
+}
+
+VAddr PageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_pages,
+ u64 needed_num_pages, std::size_t align) {
+ if (is_aslr_enabled) {
+ UNIMPLEMENTED();
+ }
+ return block_manager->FindFreeArea(start, region_num_pages, needed_num_pages, align, 0,
+ IsKernel() ? 1 : 4);
+}
+
+ResultCode PageTable::Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group,
+ OperationType operation) {
+ std::lock_guard lock{page_table_lock};
+
+ ASSERT(Common::IsAligned(addr, PageSize));
+ ASSERT(num_pages > 0);
+ ASSERT(num_pages == page_group.GetNumPages());
+
+ for (const auto& node : page_group.Nodes()) {
+ const std::size_t size{node.GetNumPages() * PageSize};
+
+ switch (operation) {
+ case OperationType::MapGroup:
+ system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ addr += size;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm,
+ OperationType operation, PAddr map_addr) {
+ std::lock_guard lock{page_table_lock};
+
+ ASSERT(num_pages > 0);
+ ASSERT(Common::IsAligned(addr, PageSize));
+ ASSERT(ContainsPages(addr, num_pages));
+
+ switch (operation) {
+ case OperationType::Unmap:
+ system.Memory().UnmapRegion(page_table_impl, addr, num_pages * PageSize);
+ break;
+ case OperationType::Map: {
+ ASSERT(map_addr);
+ ASSERT(Common::IsAligned(map_addr, PageSize));
+ system.Memory().MapMemoryRegion(page_table_impl, addr, num_pages * PageSize, map_addr);
+ break;
+ }
+ case OperationType::ChangePermissions:
+ case OperationType::ChangePermissionsAndRefresh:
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return RESULT_SUCCESS;
+}
+
+constexpr VAddr PageTable::GetRegionAddress(MemoryState state) const {
+ switch (state) {
+ case MemoryState::Free:
+ case MemoryState::Kernel:
+ return address_space_start;
+ case MemoryState::Normal:
+ return heap_region_start;
+ case MemoryState::Ipc:
+ case MemoryState::NonSecureIpc:
+ case MemoryState::NonDeviceIpc:
+ return alias_region_start;
+ case MemoryState::Stack:
+ return stack_region_start;
+ case MemoryState::Io:
+ case MemoryState::Static:
+ case MemoryState::ThreadLocal:
+ return kernel_map_region_start;
+ case MemoryState::Shared:
+ case MemoryState::AliasCode:
+ case MemoryState::AliasCodeData:
+ case MemoryState::Transfered:
+ case MemoryState::SharedTransfered:
+ case MemoryState::SharedCode:
+ case MemoryState::GeneratedCode:
+ case MemoryState::CodeOut:
+ return alias_code_region_start;
+ case MemoryState::Code:
+ case MemoryState::CodeData:
+ return code_region_start;
+ default:
+ UNREACHABLE();
+ return {};
+ }
+}
+
+constexpr std::size_t PageTable::GetRegionSize(MemoryState state) const {
+ switch (state) {
+ case MemoryState::Free:
+ case MemoryState::Kernel:
+ return address_space_end - address_space_start;
+ case MemoryState::Normal:
+ return heap_region_end - heap_region_start;
+ case MemoryState::Ipc:
+ case MemoryState::NonSecureIpc:
+ case MemoryState::NonDeviceIpc:
+ return alias_region_end - alias_region_start;
+ case MemoryState::Stack:
+ return stack_region_end - stack_region_start;
+ case MemoryState::Io:
+ case MemoryState::Static:
+ case MemoryState::ThreadLocal:
+ return kernel_map_region_end - kernel_map_region_start;
+ case MemoryState::Shared:
+ case MemoryState::AliasCode:
+ case MemoryState::AliasCodeData:
+ case MemoryState::Transfered:
+ case MemoryState::SharedTransfered:
+ case MemoryState::SharedCode:
+ case MemoryState::GeneratedCode:
+ case MemoryState::CodeOut:
+ return alias_code_region_end - alias_code_region_start;
+ case MemoryState::Code:
+ case MemoryState::CodeData:
+ return code_region_end - code_region_start;
+ default:
+ UNREACHABLE();
+ return {};
+ }
+}
+
+constexpr bool PageTable::CanContain(VAddr addr, std::size_t size, MemoryState state) const {
+ const VAddr end{addr + size};
+ const VAddr last{end - 1};
+ const VAddr region_start{GetRegionAddress(state)};
+ const std::size_t region_size{GetRegionSize(state)};
+ const bool is_in_region{region_start <= addr && addr < end &&
+ last <= region_start + region_size - 1};
+ const bool is_in_heap{!(end <= heap_region_start || heap_region_end <= addr)};
+ const bool is_in_alias{!(end <= alias_region_start || alias_region_end <= addr)};
+
+ switch (state) {
+ case MemoryState::Free:
+ case MemoryState::Kernel:
+ return is_in_region;
+ case MemoryState::Io:
+ case MemoryState::Static:
+ case MemoryState::Code:
+ case MemoryState::CodeData:
+ case MemoryState::Shared:
+ case MemoryState::AliasCode:
+ case MemoryState::AliasCodeData:
+ case MemoryState::Stack:
+ case MemoryState::ThreadLocal:
+ case MemoryState::Transfered:
+ case MemoryState::SharedTransfered:
+ case MemoryState::SharedCode:
+ case MemoryState::GeneratedCode:
+ case MemoryState::CodeOut:
+ return is_in_region && !is_in_heap && !is_in_alias;
+ case MemoryState::Normal:
+ ASSERT(is_in_heap);
+ return is_in_region && !is_in_alias;
+ case MemoryState::Ipc:
+ case MemoryState::NonSecureIpc:
+ case MemoryState::NonDeviceIpc:
+ ASSERT(is_in_alias);
+ return is_in_region && !is_in_heap;
+ default:
+ return false;
+ }
+}
+
+constexpr ResultCode PageTable::CheckMemoryState(const MemoryInfo& info, MemoryState state_mask,
+ MemoryState state, MemoryPermission perm_mask,
+ MemoryPermission perm, MemoryAttribute attr_mask,
+ MemoryAttribute attr) const {
+ // Validate the states match expectation
+ if ((info.state & state_mask) != state) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+ if ((info.perm & perm_mask) != perm) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+ if ((info.attribute & attr_mask) != attr) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm,
+ MemoryAttribute* out_attr, VAddr addr, std::size_t size,
+ MemoryState state_mask, MemoryState state,
+ MemoryPermission perm_mask, MemoryPermission perm,
+ MemoryAttribute attr_mask, MemoryAttribute attr,
+ MemoryAttribute ignore_attr) {
+ std::lock_guard lock{page_table_lock};
+
+ // Get information about the first block
+ const VAddr last_addr{addr + size - 1};
+ MemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
+ MemoryInfo info{it->GetMemoryInfo()};
+
+ // Validate all blocks in the range have correct state
+ const MemoryState first_state{info.state};
+ const MemoryPermission first_perm{info.perm};
+ const MemoryAttribute first_attr{info.attribute};
+
+ while (true) {
+ // Validate the current block
+ if (!(info.state == first_state)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+ if (!(info.perm == first_perm)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+ if (!((info.attribute | static_cast<MemoryAttribute>(ignore_attr)) ==
+ (first_attr | static_cast<MemoryAttribute>(ignore_attr)))) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ // Validate against the provided masks
+ CASCADE_CODE(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
+
+ // Break once we're done
+ if (last_addr <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance our iterator
+ it++;
+ ASSERT(it != block_manager->cend());
+ info = it->GetMemoryInfo();
+ }
+
+ // Write output state
+ if (out_state) {
+ *out_state = first_state;
+ }
+ if (out_perm) {
+ *out_perm = first_perm;
+ }
+ if (out_attr) {
+ *out_attr = first_attr & static_cast<MemoryAttribute>(~ignore_attr);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/page_table.h b/src/core/hle/kernel/memory/page_table.h
new file mode 100644
index 000000000..ce0d38849
--- /dev/null
+++ b/src/core/hle/kernel/memory/page_table.h
@@ -0,0 +1,277 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "common/page_table.h"
+#include "core/file_sys/program_metadata.h"
+#include "core/hle/kernel/memory/memory_block.h"
+#include "core/hle/kernel/memory/memory_manager.h"
+#include "core/hle/result.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel::Memory {
+
+class MemoryBlockManager;
+
+class PageTable final : NonCopyable {
+public:
+ explicit PageTable(Core::System& system);
+
+ ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
+ VAddr code_addr, std::size_t code_size,
+ Memory::MemoryManager::Pool pool);
+ ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, MemoryState state,
+ MemoryPermission perm);
+ ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
+ ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
+ ResultCode UnmapMemory(VAddr addr, std::size_t size);
+ ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state,
+ MemoryPermission perm);
+ ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm);
+ MemoryInfo QueryInfo(VAddr addr);
+ ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm);
+ ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
+ ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask,
+ MemoryAttribute value);
+ ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
+ ResultVal<VAddr> SetHeapSize(std::size_t size);
+ ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
+ bool is_map_only, VAddr region_start,
+ std::size_t region_num_pages, MemoryState state,
+ MemoryPermission perm, PAddr map_addr = 0);
+ ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
+
+ Common::PageTable& PageTableImpl() {
+ return page_table_impl;
+ }
+
+ const Common::PageTable& PageTableImpl() const {
+ return page_table_impl;
+ }
+
+private:
+ enum class OperationType : u32 {
+ Map,
+ MapGroup,
+ Unmap,
+ ChangePermissions,
+ ChangePermissionsAndRefresh,
+ };
+
+ static constexpr MemoryAttribute DefaultMemoryIgnoreAttr =
+ MemoryAttribute::DontCareMask | MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared;
+
+ ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
+ ResultCode MapPages(VAddr addr, const PageLinkedList& page_linked_list, MemoryPermission perm);
+ void MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end);
+ bool IsRegionMapped(VAddr address, u64 size);
+ bool IsRegionContiguous(VAddr addr, u64 size) const;
+ void AddRegionToPages(VAddr start, std::size_t num_pages, PageLinkedList& page_linked_list);
+ MemoryInfo QueryInfoImpl(VAddr addr);
+ VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages,
+ std::size_t align);
+ ResultCode Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group,
+ OperationType operation);
+ ResultCode Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm,
+ OperationType operation, PAddr map_addr = 0);
+ constexpr VAddr GetRegionAddress(MemoryState state) const;
+ constexpr std::size_t GetRegionSize(MemoryState state) const;
+ constexpr bool CanContain(VAddr addr, std::size_t size, MemoryState state) const;
+
+ constexpr ResultCode CheckMemoryState(const MemoryInfo& info, MemoryState state_mask,
+ MemoryState state, MemoryPermission perm_mask,
+ MemoryPermission perm, MemoryAttribute attr_mask,
+ MemoryAttribute attr) const;
+ ResultCode CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm,
+ MemoryAttribute* out_attr, VAddr addr, std::size_t size,
+ MemoryState state_mask, MemoryState state,
+ MemoryPermission perm_mask, MemoryPermission perm,
+ MemoryAttribute attr_mask, MemoryAttribute attr,
+ MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
+ ResultCode CheckMemoryState(VAddr addr, std::size_t size, MemoryState state_mask,
+ MemoryState state, MemoryPermission perm_mask,
+ MemoryPermission perm, MemoryAttribute attr_mask,
+ MemoryAttribute attr,
+ MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
+ return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
+ perm, attr_mask, attr, ignore_attr);
+ }
+
+ std::recursive_mutex page_table_lock;
+ std::unique_ptr<MemoryBlockManager> block_manager;
+
+public:
+ constexpr VAddr GetAddressSpaceStart() const {
+ return address_space_start;
+ }
+ constexpr VAddr GetAddressSpaceEnd() const {
+ return address_space_end;
+ }
+ constexpr std::size_t GetAddressSpaceSize() const {
+ return address_space_end - address_space_start;
+ }
+ constexpr VAddr GetHeapRegionStart() const {
+ return heap_region_start;
+ }
+ constexpr VAddr GetHeapRegionEnd() const {
+ return heap_region_end;
+ }
+ constexpr std::size_t GetHeapRegionSize() const {
+ return heap_region_end - heap_region_start;
+ }
+ constexpr VAddr GetAliasRegionStart() const {
+ return alias_region_start;
+ }
+ constexpr VAddr GetAliasRegionEnd() const {
+ return alias_region_end;
+ }
+ constexpr std::size_t GetAliasRegionSize() const {
+ return alias_region_end - alias_region_start;
+ }
+ constexpr VAddr GetStackRegionStart() const {
+ return stack_region_start;
+ }
+ constexpr VAddr GetStackRegionEnd() const {
+ return stack_region_end;
+ }
+ constexpr std::size_t GetStackRegionSize() const {
+ return stack_region_end - stack_region_start;
+ }
+ constexpr VAddr GetKernelMapRegionStart() const {
+ return kernel_map_region_start;
+ }
+ constexpr VAddr GetKernelMapRegionEnd() const {
+ return kernel_map_region_end;
+ }
+ constexpr VAddr GetCodeRegionStart() const {
+ return code_region_start;
+ }
+ constexpr VAddr GetCodeRegionEnd() const {
+ return code_region_end;
+ }
+ constexpr VAddr GetAliasCodeRegionStart() const {
+ return alias_code_region_start;
+ }
+ constexpr VAddr GetAliasCodeRegionSize() const {
+ return alias_code_region_end - alias_code_region_start;
+ }
+ constexpr std::size_t GetAddressSpaceWidth() const {
+ return address_space_width;
+ }
+ constexpr std::size_t GetHeapSize() {
+ return current_heap_addr - heap_region_start;
+ }
+ constexpr std::size_t GetTotalHeapSize() {
+ return GetHeapSize() + physical_memory_usage;
+ }
+ constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const {
+ return address_space_start <= address && address + size - 1 <= address_space_end - 1;
+ }
+ constexpr bool IsOutsideAliasRegion(VAddr address, std::size_t size) const {
+ return alias_region_start > address || address + size - 1 > alias_region_end - 1;
+ }
+ constexpr bool IsOutsideStackRegion(VAddr address, std::size_t size) const {
+ return stack_region_start > address || address + size - 1 > stack_region_end - 1;
+ }
+ constexpr bool IsInvalidRegion(VAddr address, std::size_t size) const {
+ return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1;
+ }
+ constexpr bool IsInsideHeapRegion(VAddr address, std::size_t size) const {
+ return address + size > heap_region_start && heap_region_end > address;
+ }
+ constexpr bool IsInsideAliasRegion(VAddr address, std::size_t size) const {
+ return address + size > alias_region_start && alias_region_end > address;
+ }
+ constexpr bool IsOutsideASLRRegion(VAddr address, std::size_t size) const {
+ if (IsInvalidRegion(address, size)) {
+ return true;
+ }
+ if (IsInsideHeapRegion(address, size)) {
+ return true;
+ }
+ if (IsInsideAliasRegion(address, size)) {
+ return true;
+ }
+ return {};
+ }
+ constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
+ return !IsOutsideASLRRegion(address, size);
+ }
+ constexpr PAddr GetPhysicalAddr(VAddr addr) {
+ return page_table_impl.backing_addr[addr >> Memory::PageBits] + addr;
+ }
+
+private:
+ constexpr bool Contains(VAddr addr) const {
+ return address_space_start <= addr && addr <= address_space_end - 1;
+ }
+ constexpr bool Contains(VAddr addr, std::size_t size) const {
+ return address_space_start <= addr && addr < addr + size &&
+ addr + size - 1 <= address_space_end - 1;
+ }
+ constexpr bool IsKernel() const {
+ return is_kernel;
+ }
+ constexpr bool IsAslrEnabled() const {
+ return is_aslr_enabled;
+ }
+
+ constexpr std::size_t GetNumGuardPages() const {
+ return IsKernel() ? 1 : 4;
+ }
+
+ constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const {
+ return (address_space_start <= addr) &&
+ (num_pages <= (address_space_end - address_space_start) / PageSize) &&
+ (addr + num_pages * PageSize - 1 <= address_space_end - 1);
+ }
+
+private:
+ VAddr address_space_start{};
+ VAddr address_space_end{};
+ VAddr heap_region_start{};
+ VAddr heap_region_end{};
+ VAddr current_heap_end{};
+ VAddr alias_region_start{};
+ VAddr alias_region_end{};
+ VAddr stack_region_start{};
+ VAddr stack_region_end{};
+ VAddr kernel_map_region_start{};
+ VAddr kernel_map_region_end{};
+ VAddr code_region_start{};
+ VAddr code_region_end{};
+ VAddr alias_code_region_start{};
+ VAddr alias_code_region_end{};
+ VAddr current_heap_addr{};
+
+ std::size_t heap_capacity{};
+ std::size_t physical_memory_usage{};
+ std::size_t max_heap_size{};
+ std::size_t max_physical_memory_size{};
+ std::size_t address_space_width{};
+
+ bool is_kernel{};
+ bool is_aslr_enabled{};
+
+ MemoryManager::Pool memory_pool{MemoryManager::Pool::Application};
+
+ Common::PageTable page_table_impl;
+
+ Core::System& system;
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/slab_heap.h b/src/core/hle/kernel/memory/slab_heap.h
new file mode 100644
index 000000000..465eaddb3
--- /dev/null
+++ b/src/core/hle/kernel/memory/slab_heap.h
@@ -0,0 +1,163 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+// This file references various implementation details from Atmosphere, an open-source firmware for
+// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+
+#pragma once
+
+#include <atomic>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+
+namespace Kernel::Memory {
+
+namespace impl {
+
+class SlabHeapImpl final : NonCopyable {
+public:
+ struct Node {
+ Node* next{};
+ };
+
+ constexpr SlabHeapImpl() = default;
+
+ void Initialize(std::size_t size) {
+ ASSERT(head == nullptr);
+ obj_size = size;
+ }
+
+ constexpr std::size_t GetObjectSize() const {
+ return obj_size;
+ }
+
+ Node* GetHead() const {
+ return head;
+ }
+
+ void* Allocate() {
+ Node* ret = head.load();
+
+ do {
+ if (ret == nullptr) {
+ break;
+ }
+ } while (!head.compare_exchange_weak(ret, ret->next));
+
+ return ret;
+ }
+
+ void Free(void* obj) {
+ Node* node = static_cast<Node*>(obj);
+
+ Node* cur_head = head.load();
+ do {
+ node->next = cur_head;
+ } while (!head.compare_exchange_weak(cur_head, node));
+ }
+
+private:
+ std::atomic<Node*> head{};
+ std::size_t obj_size{};
+};
+
+} // namespace impl
+
+class SlabHeapBase : NonCopyable {
+public:
+ constexpr SlabHeapBase() = default;
+
+ constexpr bool Contains(uintptr_t addr) const {
+ return start <= addr && addr < end;
+ }
+
+ constexpr std::size_t GetSlabHeapSize() const {
+ return (end - start) / GetObjectSize();
+ }
+
+ constexpr std::size_t GetObjectSize() const {
+ return impl.GetObjectSize();
+ }
+
+ constexpr uintptr_t GetSlabHeapAddress() const {
+ return start;
+ }
+
+ std::size_t GetObjectIndexImpl(const void* obj) const {
+ return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize();
+ }
+
+ std::size_t GetPeakIndex() const {
+ return GetObjectIndexImpl(reinterpret_cast<const void*>(peak));
+ }
+
+ void* AllocateImpl() {
+ return impl.Allocate();
+ }
+
+ void FreeImpl(void* obj) {
+ // Don't allow freeing an object that wasn't allocated from this heap
+ ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
+ impl.Free(obj);
+ }
+
+ void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) {
+ // Ensure we don't initialize a slab using null memory
+ ASSERT(memory != nullptr);
+
+ // Initialize the base allocator
+ impl.Initialize(obj_size);
+
+ // Set our tracking variables
+ const std::size_t num_obj = (memory_size / obj_size);
+ start = reinterpret_cast<uintptr_t>(memory);
+ end = start + num_obj * obj_size;
+ peak = start;
+
+ // Free the objects
+ u8* cur = reinterpret_cast<u8*>(end);
+
+ for (std::size_t i{}; i < num_obj; i++) {
+ cur -= obj_size;
+ impl.Free(cur);
+ }
+ }
+
+private:
+ using Impl = impl::SlabHeapImpl;
+
+ Impl impl;
+ uintptr_t peak{};
+ uintptr_t start{};
+ uintptr_t end{};
+};
+
+template <typename T>
+class SlabHeap final : public SlabHeapBase {
+public:
+ constexpr SlabHeap() : SlabHeapBase() {}
+
+ void Initialize(void* memory, std::size_t memory_size) {
+ InitializeImpl(sizeof(T), memory, memory_size);
+ }
+
+ T* Allocate() {
+ T* obj = static_cast<T*>(AllocateImpl());
+ if (obj != nullptr) {
+ new (obj) T();
+ }
+ return obj;
+ }
+
+ void Free(T* obj) {
+ FreeImpl(obj);
+ }
+
+ constexpr std::size_t GetObjectIndex(const T* obj) const {
+ return GetObjectIndexImpl(obj);
+ }
+};
+
+} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/system_control.cpp b/src/core/hle/kernel/memory/system_control.cpp
new file mode 100644
index 000000000..11d204bc2
--- /dev/null
+++ b/src/core/hle/kernel/memory/system_control.cpp
@@ -0,0 +1,40 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <random>
+
+#include "core/hle/kernel/memory/system_control.h"
+
+namespace Kernel::Memory::SystemControl {
+namespace {
+template <typename F>
+u64 GenerateUniformRange(u64 min, u64 max, F f) {
+ // Handle the case where the difference is too large to represent.
+ if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
+ return f();
+ }
+
+ // Iterate until we get a value in range.
+ const u64 range_size = ((max + 1) - min);
+ const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
+ while (true) {
+ if (const u64 rnd = f(); rnd < effective_max) {
+ return min + (rnd % range_size);
+ }
+ }
+}
+
+u64 GenerateRandomU64ForInit() {
+ static std::random_device device;
+ static std::mt19937 gen(device());
+ static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
+ return distribution(gen);
+}
+} // Anonymous namespace
+
+u64 GenerateRandomRange(u64 min, u64 max) {
+ return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
+}
+
+} // namespace Kernel::Memory::SystemControl
diff --git a/src/core/hle/kernel/memory/system_control.h b/src/core/hle/kernel/memory/system_control.h
new file mode 100644
index 000000000..19cab8cbc
--- /dev/null
+++ b/src/core/hle/kernel/memory/system_control.h
@@ -0,0 +1,13 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Kernel::Memory::SystemControl {
+
+u64 GenerateRandomRange(u64 min, u64 max);
+
+} // namespace Kernel::Memory::SystemControl
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index eff4e45b0..8f6c944d1 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -7,6 +7,7 @@
#include <vector>
#include "common/assert.h"
+#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
@@ -33,8 +34,6 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr
if (thread->GetMutexWaitAddress() != mutex_addr)
continue;
- ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
-
++num_waiters;
if (highest_priority_thread == nullptr ||
thread->GetPriority() < highest_priority_thread->GetPriority()) {
@@ -48,6 +47,7 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread,
std::shared_ptr<Thread> new_owner) {
+ current_thread->RemoveMutexWaiter(new_owner);
const auto threads = current_thread->GetMutexWaitingThreads();
for (const auto& thread : threads) {
if (thread->GetMutexWaitAddress() != mutex_addr)
@@ -67,85 +67,104 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
+ LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ auto& kernel = system.Kernel();
std::shared_ptr<Thread> current_thread =
- SharedFrom(system.CurrentScheduler().GetCurrentThread());
- std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
- std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
+ SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
+ {
+ SchedulerLock lock(kernel);
+ // The mutex address must be 4-byte aligned
+ if ((address % sizeof(u32)) != 0) {
+ return ERR_INVALID_ADDRESS;
+ }
- // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
- // thread.
- ASSERT(requesting_thread == current_thread);
+ const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+ std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
+ std::shared_ptr<Thread> requesting_thread =
+ handle_table.Get<Thread>(requesting_thread_handle);
- const u32 addr_value = system.Memory().Read32(address);
+ // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of
+ // another thread.
+ ASSERT(requesting_thread == current_thread);
- // If the mutex isn't being held, just return success.
- if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
- return RESULT_SUCCESS;
- }
+ current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
- if (holding_thread == nullptr) {
- return ERR_INVALID_HANDLE;
- }
+ const u32 addr_value = system.Memory().Read32(address);
+
+ // If the mutex isn't being held, just return success.
+ if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
+ return RESULT_SUCCESS;
+ }
- // Wait until the mutex is released
- current_thread->SetMutexWaitAddress(address);
- current_thread->SetWaitHandle(requesting_thread_handle);
+ if (holding_thread == nullptr) {
+ return ERR_INVALID_HANDLE;
+ }
- current_thread->SetStatus(ThreadStatus::WaitMutex);
- current_thread->InvalidateWakeupCallback();
+ // Wait until the mutex is released
+ current_thread->SetMutexWaitAddress(address);
+ current_thread->SetWaitHandle(requesting_thread_handle);
- // Update the lock holder thread's priority to prevent priority inversion.
- holding_thread->AddMutexWaiter(current_thread);
+ current_thread->SetStatus(ThreadStatus::WaitMutex);
- system.PrepareReschedule();
+ // Update the lock holder thread's priority to prevent priority inversion.
+ holding_thread->AddMutexWaiter(current_thread);
+ }
- return RESULT_SUCCESS;
+ {
+ SchedulerLock lock(kernel);
+ auto* owner = current_thread->GetLockOwner();
+ if (owner != nullptr) {
+ owner->RemoveMutexWaiter(current_thread);
+ }
+ }
+ return current_thread->GetSignalingResult();
}
-ResultCode Mutex::Release(VAddr address) {
+std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner,
+ VAddr address) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
- return ERR_INVALID_ADDRESS;
+ LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
+ return {ERR_INVALID_ADDRESS, nullptr};
}
- std::shared_ptr<Thread> current_thread =
- SharedFrom(system.CurrentScheduler().GetCurrentThread());
- auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
-
- // There are no more threads waiting for the mutex, release it completely.
- if (thread == nullptr) {
+ auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address);
+ if (new_owner == nullptr) {
system.Memory().Write32(address, 0);
- return RESULT_SUCCESS;
+ return {RESULT_SUCCESS, nullptr};
}
-
// Transfer the ownership of the mutex from the previous owner to the new one.
- TransferMutexOwnership(address, current_thread, thread);
-
- u32 mutex_value = thread->GetWaitHandle();
-
+ TransferMutexOwnership(address, owner, new_owner);
+ u32 mutex_value = new_owner->GetWaitHandle();
if (num_waiters >= 2) {
// Notify the guest that there are still some threads waiting for the mutex
mutex_value |= Mutex::MutexHasWaitersFlag;
}
+ new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
+ new_owner->SetLockOwner(nullptr);
+ new_owner->ResumeFromWait();
- // Grant the mutex to the next waiting thread and resume it.
system.Memory().Write32(address, mutex_value);
+ return {RESULT_SUCCESS, new_owner};
+}
- ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
- thread->ResumeFromWait();
+ResultCode Mutex::Release(VAddr address) {
+ auto& kernel = system.Kernel();
+ SchedulerLock lock(kernel);
- thread->SetLockOwner(nullptr);
- thread->SetCondVarWaitAddress(0);
- thread->SetMutexWaitAddress(0);
- thread->SetWaitHandle(0);
- thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
+ std::shared_ptr<Thread> current_thread =
+ SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
- system.PrepareReschedule();
+ auto [result, new_owner] = Unlock(current_thread, address);
- return RESULT_SUCCESS;
+ if (result != RESULT_SUCCESS && new_owner != nullptr) {
+ new_owner->SetSynchronizationResults(nullptr, result);
+ }
+
+ return result;
}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index b904de2e8..3b81dc3df 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -28,6 +28,10 @@ public:
ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle);
+ /// Unlocks a mutex for owner at address
+ std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner,
+ VAddr address);
+
/// Releases the mutex at the specified address.
ResultCode Release(VAddr address);
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index aa2787467..6e04d025f 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -2,63 +2,43 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/logging/log.h"
-#include "core/arm/arm_interface.h"
-#ifdef ARCHITECTURE_x86_64
-#include "core/arm/dynarmic/arm_dynarmic_32.h"
-#include "core/arm/dynarmic/arm_dynarmic_64.h"
-#endif
-#include "core/arm/exclusive_monitor.h"
-#include "core/arm/unicorn/arm_unicorn.h"
+#include "common/spin_lock.h"
+#include "core/arm/cpu_interrupt_handler.h"
#include "core/core.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/scheduler.h"
-#include "core/hle/kernel/thread.h"
namespace Kernel {
-PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
- Core::ExclusiveMonitor& exclusive_monitor)
- : core_index{id} {
-#ifdef ARCHITECTURE_x86_64
- arm_interface_32 =
- std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index);
- arm_interface_64 =
- std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index);
-
-#else
- arm_interface = std::make_shared<Core::ARM_Unicorn>(system);
- LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
-#endif
-
- scheduler = std::make_unique<Kernel::Scheduler>(system, core_index);
-}
+PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler,
+ Core::CPUInterruptHandler& interrupt_handler)
+ : interrupt_handler{interrupt_handler},
+ core_index{id}, scheduler{scheduler}, guard{std::make_unique<Common::SpinLock>()} {}
PhysicalCore::~PhysicalCore() = default;
-void PhysicalCore::Run() {
- arm_interface->Run();
- arm_interface->ClearExclusiveState();
+void PhysicalCore::Idle() {
+ interrupt_handler.AwaitInterrupt();
}
-void PhysicalCore::Step() {
- arm_interface->Step();
+void PhysicalCore::Shutdown() {
+ scheduler.Shutdown();
}
-void PhysicalCore::Stop() {
- arm_interface->PrepareReschedule();
+bool PhysicalCore::IsInterrupted() const {
+ return interrupt_handler.IsInterrupted();
}
-void PhysicalCore::Shutdown() {
- scheduler->Shutdown();
+void PhysicalCore::Interrupt() {
+ guard->lock();
+ interrupt_handler.SetInterrupt(true);
+ guard->unlock();
}
-void PhysicalCore::SetIs64Bit(bool is_64_bit) {
- if (is_64_bit) {
- arm_interface = arm_interface_64.get();
- } else {
- arm_interface = arm_interface_32.get();
- }
+void PhysicalCore::ClearInterrupt() {
+ guard->lock();
+ interrupt_handler.SetInterrupt(false);
+ guard->unlock();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 3269166be..d7a7a951c 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -7,12 +7,17 @@
#include <cstddef>
#include <memory>
+namespace Common {
+class SpinLock;
+}
+
namespace Kernel {
class Scheduler;
} // namespace Kernel
namespace Core {
class ARM_Interface;
+class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -21,7 +26,8 @@ namespace Kernel {
class PhysicalCore {
public:
- PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor);
+ PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler,
+ Core::CPUInterruptHandler& interrupt_handler);
~PhysicalCore();
PhysicalCore(const PhysicalCore&) = delete;
@@ -30,23 +36,18 @@ public:
PhysicalCore(PhysicalCore&&) = default;
PhysicalCore& operator=(PhysicalCore&&) = default;
- /// Execute current jit state
- void Run();
- /// Execute a single instruction in current jit.
- void Step();
- /// Stop JIT execution/exit
- void Stop();
+ void Idle();
+ /// Interrupt this physical core.
+ void Interrupt();
- // Shutdown this physical core.
- void Shutdown();
+ /// Clear this core's interrupt
+ void ClearInterrupt();
- Core::ARM_Interface& ArmInterface() {
- return *arm_interface;
- }
+ /// Check if this core is interrupted
+ bool IsInterrupted() const;
- const Core::ARM_Interface& ArmInterface() const {
- return *arm_interface;
- }
+ // Shutdown this physical core.
+ void Shutdown();
bool IsMainCore() const {
return core_index == 0;
@@ -61,21 +62,18 @@ public:
}
Kernel::Scheduler& Scheduler() {
- return *scheduler;
+ return scheduler;
}
const Kernel::Scheduler& Scheduler() const {
- return *scheduler;
+ return scheduler;
}
- void SetIs64Bit(bool is_64_bit);
-
private:
+ Core::CPUInterruptHandler& interrupt_handler;
std::size_t core_index;
- std::unique_ptr<Core::ARM_Interface> arm_interface_32;
- std::unique_ptr<Core::ARM_Interface> arm_interface_64;
- std::unique_ptr<Kernel::Scheduler> scheduler;
- Core::ARM_Interface* arm_interface{};
+ Kernel::Scheduler& scheduler;
+ std::unique_ptr<Common::SpinLock> guard;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h
index b689e8e8b..7a0266780 100644
--- a/src/core/hle/kernel/physical_memory.h
+++ b/src/core/hle/kernel/physical_memory.h
@@ -4,6 +4,8 @@
#pragma once
+#include <vector>
+
#include "common/alignment.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index edc414d69..b17529dee 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -4,21 +4,26 @@
#include <algorithm>
#include <bitset>
+#include <ctime>
#include <memory>
#include <random>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/device_memory.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory/memory_block_manager.h"
+#include "core/hle/kernel/memory/page_table.h"
+#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/vm_manager.h"
+#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/settings.h"
@@ -27,26 +32,31 @@ namespace {
/**
* Sets up the primary application thread
*
+ * @param system The system instance to create the main thread under.
* @param owner_process The parent process for the main thread
- * @param kernel The kernel instance to create the main thread under.
* @param priority The priority to give the main thread
*/
-void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
- const auto& vm_manager = owner_process.VMManager();
- const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress();
- const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress();
- auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
- owner_process.GetIdealCore(), stack_top, owner_process);
+void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
+ const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
+ ThreadType type = THREADTYPE_USER;
+ auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0,
+ owner_process.GetIdealCore(), stack_top, &owner_process);
std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
+ thread->GetContext32().cpu_registers[0] = 0;
+ thread->GetContext64().cpu_registers[0] = 0;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
+ auto& kernel = system.Kernel();
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
- thread->ResumeFromWait();
+ {
+ SchedulerLock lock{kernel};
+ thread->SetStatus(ThreadStatus::Ready);
+ }
}
} // Anonymous namespace
@@ -57,7 +67,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
// (whichever page happens to have an available slot).
class TLSPage {
public:
- static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE;
+ static constexpr std::size_t num_slot_entries =
+ Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;
explicit TLSPage(VAddr address) : base_address{address} {}
@@ -76,7 +87,7 @@ public:
}
is_slot_used[i] = true;
- return base_address + (i * Memory::TLS_ENTRY_SIZE);
+ return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);
}
return std::nullopt;
@@ -86,15 +97,15 @@ public:
// Ensure that all given addresses are consistent with how TLS pages
// are intended to be used when releasing slots.
ASSERT(IsWithinPage(address));
- ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0);
+ ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0);
- const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE;
+ const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;
is_slot_used[index] = false;
}
private:
bool IsWithinPage(VAddr address) const {
- return base_address <= address && address < base_address + Memory::PAGE_SIZE;
+ return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;
}
VAddr base_address;
@@ -106,14 +117,14 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
std::shared_ptr<Process> process = std::make_shared<Process>(system);
process->name = std::move(name);
- process->resource_limit = kernel.GetSystemResourceLimit();
+ process->resource_limit = ResourceLimit::Create(kernel);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
: kernel.CreateNewUserProcessID();
process->capabilities.InitializeForMetadatalessProcess();
- std::mt19937 rng(Settings::values.rng_seed.value_or(0));
+ std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr)));
std::uniform_int_distribution<u64> distribution;
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
[&] { return distribution(rng); });
@@ -127,7 +138,15 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
}
u64 Process::GetTotalPhysicalMemoryAvailable() const {
- return vm_manager.GetTotalPhysicalMemoryAvailable();
+ const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) +
+ page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
+ main_thread_stack_size};
+
+ if (capacity < memory_usage_capacity) {
+ return capacity;
+ }
+
+ return memory_usage_capacity;
}
u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
@@ -135,8 +154,8 @@ u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
}
u64 Process::GetTotalPhysicalMemoryUsed() const {
- return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size +
- GetSystemResourceUsage();
+ return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
+ GetSystemResourceSize();
}
u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
@@ -170,7 +189,6 @@ void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
}
++it;
}
- UNREACHABLE();
}
std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(
@@ -195,6 +213,7 @@ void Process::UnregisterThread(const Thread* thread) {
}
ResultCode Process::ClearSignalState() {
+ SchedulerLock lock(system.Kernel());
if (status == ProcessStatus::Exited) {
LOG_ERROR(Kernel, "called on a terminated process instance.");
return ERR_INVALID_STATE;
@@ -209,33 +228,82 @@ ResultCode Process::ClearSignalState() {
return RESULT_SUCCESS;
}
-ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
+ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
+ std::size_t code_size) {
program_id = metadata.GetTitleID();
ideal_core = metadata.GetMainThreadCore();
is_64bit_process = metadata.Is64BitProgram();
system_resource_size = metadata.GetSystemResourceSize();
+ image_size = code_size;
+
+ // Initialize proces address space
+ if (const ResultCode result{
+ page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000,
+ code_size, Memory::MemoryManager::Pool::Application)};
+ result.IsError()) {
+ return result;
+ }
- vm_manager.Reset(metadata.GetAddressSpaceType());
+ // Map process code region
+ if (const ResultCode result{page_table->MapProcessCode(
+ page_table->GetCodeRegionStart(), code_size / Memory::PageSize,
+ Memory::MemoryState::Code, Memory::MemoryPermission::None)};
+ result.IsError()) {
+ return result;
+ }
+
+ // Initialize process capabilities
+ const auto& caps{metadata.GetKernelCapabilities()};
+ if (const ResultCode result{
+ capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)};
+ result.IsError()) {
+ return result;
+ }
- const auto& caps = metadata.GetKernelCapabilities();
- const auto capability_init_result =
- capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
- if (capability_init_result.IsError()) {
- return capability_init_result;
+ // Set memory usage capacity
+ switch (metadata.GetAddressSpaceType()) {
+ case FileSys::ProgramAddressSpaceType::Is32Bit:
+ case FileSys::ProgramAddressSpaceType::Is36Bit:
+ case FileSys::ProgramAddressSpaceType::Is39Bit:
+ memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart();
+ break;
+
+ case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
+ memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart() +
+ page_table->GetAliasRegionEnd() - page_table->GetAliasRegionStart();
+ break;
+
+ default:
+ UNREACHABLE();
}
+ // Set initial resource limits
+ resource_limit->SetLimitValue(
+ ResourceType::PhysicalMemory,
+ kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
+ resource_limit->SetLimitValue(ResourceType::Threads, 608);
+ resource_limit->SetLimitValue(ResourceType::Events, 700);
+ resource_limit->SetLimitValue(ResourceType::TransferMemory, 128);
+ resource_limit->SetLimitValue(ResourceType::Sessions, 894);
+ ASSERT(resource_limit->Reserve(ResourceType::PhysicalMemory, code_size));
+
+ // Create TLS region
+ tls_region_address = CreateTLSRegion();
+
return handle_table.SetSize(capabilities.GetHandleTableSize());
}
void Process::Run(s32 main_thread_priority, u64 stack_size) {
AllocateMainThreadStack(stack_size);
- tls_region_address = CreateTLSRegion();
- vm_manager.LogLayout();
+ const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
+ ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
ChangeStatus(ProcessStatus::Running);
- SetupMainThread(*this, kernel, main_thread_priority);
+ SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top);
+ resource_limit->Reserve(ResourceType::Threads, 1);
+ resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size);
}
void Process::PrepareForTermination() {
@@ -279,32 +347,39 @@ static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
}
VAddr Process::CreateTLSRegion() {
- auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages);
+ SchedulerLock lock(system.Kernel());
+ if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)};
+ tls_page_iter != tls_pages.cend()) {
+ return *tls_page_iter->ReserveSlot();
+ }
- if (tls_page_iter == tls_pages.cend()) {
- const auto region_address =
- vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(),
- vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE);
- ASSERT(region_address.Succeeded());
+ Memory::Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
+ ASSERT(tls_page_ptr);
- const auto map_result = vm_manager.MapMemoryBlock(
- *region_address, std::make_shared<PhysicalMemory>(Memory::PAGE_SIZE), 0,
- Memory::PAGE_SIZE, MemoryState::ThreadLocal);
- ASSERT(map_result.Succeeded());
+ const VAddr start{page_table->GetKernelMapRegionStart()};
+ const VAddr size{page_table->GetKernelMapRegionEnd() - start};
+ const PAddr tls_map_addr{system.DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
+ const VAddr tls_page_addr{
+ page_table
+ ->AllocateAndMapMemory(1, Memory::PageSize, true, start, size / Memory::PageSize,
+ Memory::MemoryState::ThreadLocal,
+ Memory::MemoryPermission::ReadAndWrite, tls_map_addr)
+ .ValueOr(0)};
- tls_pages.emplace_back(*region_address);
+ ASSERT(tls_page_addr);
- const auto reserve_result = tls_pages.back().ReserveSlot();
- ASSERT(reserve_result.has_value());
+ std::memset(tls_page_ptr, 0, Memory::PageSize);
+ tls_pages.emplace_back(tls_page_addr);
- return *reserve_result;
- }
+ const auto reserve_result{tls_pages.back().ReserveSlot()};
+ ASSERT(reserve_result.has_value());
- return *tls_page_iter->ReserveSlot();
+ return *reserve_result;
}
void Process::FreeTLSRegion(VAddr tls_address) {
- const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE);
+ SchedulerLock lock(system.Kernel());
+ const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
auto iter =
std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
return page.GetBaseAddress() == aligned_address;
@@ -317,29 +392,24 @@ void Process::FreeTLSRegion(VAddr tls_address) {
iter->ReleaseSlot(tls_address);
}
-void Process::LoadModule(CodeSet module_, VAddr base_addr) {
- code_memory_size += module_.memory.size();
-
- const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory));
-
- const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
- MemoryState memory_state) {
- const auto vma = vm_manager
- .MapMemoryBlock(segment.addr + base_addr, memory, segment.offset,
- segment.size, memory_state)
- .Unwrap();
- vm_manager.Reprotect(vma, permissions);
+void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
+ std::lock_guard lock{HLE::g_hle_lock};
+ const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
+ Memory::MemoryPermission permission) {
+ page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);
};
- // Map CodeSet segments
- MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
- MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
- MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
+ system.Memory().WriteBlock(*this, base_addr, code_set.memory.data(), code_set.memory.size());
+
+ ReprotectSegment(code_set.CodeSegment(), Memory::MemoryPermission::ReadAndExecute);
+ ReprotectSegment(code_set.RODataSegment(), Memory::MemoryPermission::Read);
+ ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite);
}
Process::Process(Core::System& system)
- : SynchronizationObject{system.Kernel()}, vm_manager{system},
- address_arbiter{system}, mutex{system}, system{system} {}
+ : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>(
+ system)},
+ handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
Process::~Process() = default;
@@ -361,16 +431,24 @@ void Process::ChangeStatus(ProcessStatus new_status) {
Signal();
}
-void Process::AllocateMainThreadStack(u64 stack_size) {
+ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) {
+ ASSERT(stack_size);
+
// The kernel always ensures that the given stack size is page aligned.
- main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
-
- // Allocate and map the main thread stack
- const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
- vm_manager
- .MapMemoryBlock(mapping_address, std::make_shared<PhysicalMemory>(main_thread_stack_size),
- 0, main_thread_stack_size, MemoryState::Stack)
- .Unwrap();
+ main_thread_stack_size = Common::AlignUp(stack_size, Memory::PageSize);
+
+ const VAddr start{page_table->GetStackRegionStart()};
+ const std::size_t size{page_table->GetStackRegionEnd() - start};
+
+ CASCADE_RESULT(main_thread_stack_top,
+ page_table->AllocateAndMapMemory(
+ main_thread_stack_size / Memory::PageSize, Memory::PageSize, false, start,
+ size / Memory::PageSize, Memory::MemoryState::Stack,
+ Memory::MemoryPermission::ReadAndWrite));
+
+ main_thread_stack_top += main_thread_stack_size;
+
+ return RESULT_SUCCESS;
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 4887132a7..f45cb5674 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -16,7 +16,6 @@
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/synchronization_object.h"
-#include "core/hle/kernel/vm_manager.h"
#include "core/hle/result.h"
namespace Core {
@@ -36,6 +35,10 @@ class TLSPage;
struct CodeSet;
+namespace Memory {
+class PageTable;
+}
+
enum class MemoryRegion : u16 {
APPLICATION = 1,
SYSTEM = 2,
@@ -100,14 +103,14 @@ public:
return HANDLE_TYPE;
}
- /// Gets a reference to the process' memory manager.
- Kernel::VMManager& VMManager() {
- return vm_manager;
+ /// Gets a reference to the process' page table.
+ Memory::PageTable& PageTable() {
+ return *page_table;
}
- /// Gets a const reference to the process' memory manager.
- const Kernel::VMManager& VMManager() const {
- return vm_manager;
+ /// Gets const a reference to the process' page table.
+ const Memory::PageTable& PageTable() const {
+ return *page_table;
}
/// Gets a reference to the process' handle table.
@@ -273,7 +276,7 @@ public:
* @returns RESULT_SUCCESS if all relevant metadata was able to be
* loaded and parsed. Otherwise, an error code is returned.
*/
- ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
+ ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
/**
* Starts the main application thread for this process.
@@ -289,7 +292,7 @@ public:
*/
void PrepareForTermination();
- void LoadModule(CodeSet module_, VAddr base_addr);
+ void LoadModule(CodeSet code_set, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management
@@ -313,16 +316,10 @@ private:
void ChangeStatus(ProcessStatus new_status);
/// Allocates the main thread stack for the process, given the stack size in bytes.
- void AllocateMainThreadStack(u64 stack_size);
-
- /// Memory manager for this process.
- Kernel::VMManager vm_manager;
+ ResultCode AllocateMainThreadStack(std::size_t stack_size);
- /// Size of the main thread's stack in bytes.
- u64 main_thread_stack_size = 0;
-
- /// Size of the loaded code memory in bytes.
- u64 code_memory_size = 0;
+ /// Memory manager for this process
+ std::unique_ptr<Memory::PageTable> page_table;
/// Current status of the process
ProcessStatus status{};
@@ -385,11 +382,23 @@ private:
/// List of threads waiting for a condition variable
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
- /// System context
- Core::System& system;
+ /// Address of the top of the main thread's stack
+ VAddr main_thread_stack_top{};
+
+ /// Size of the main thread's stack
+ std::size_t main_thread_stack_size{};
+
+ /// Memory usage capacity for the process
+ std::size_t memory_usage_capacity{};
+
+ /// Process total image size
+ std::size_t image_size{};
/// Name of this process
std::string name;
+
+ /// System context
+ Core::System& system;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 583e35b79..63880f13d 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -3,10 +3,11 @@
// Refer to the license.txt file included.
#include "common/bit_util.h"
+#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process_capability.h"
-#include "core/hle/kernel/vm_manager.h"
namespace Kernel {
namespace {
@@ -66,7 +67,7 @@ u32 GetFlagBitOffset(CapabilityType type) {
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
std::size_t num_capabilities,
- VMManager& vm_manager) {
+ Memory::PageTable& page_table) {
Clear();
// Allow all cores and priorities.
@@ -74,15 +75,15 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti
priority_mask = 0xFFFFFFFFFFFFFFFF;
kernel_version = PackedKernelVersion;
- return ParseCapabilities(capabilities, num_capabilities, vm_manager);
+ return ParseCapabilities(capabilities, num_capabilities, page_table);
}
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
std::size_t num_capabilities,
- VMManager& vm_manager) {
+ Memory::PageTable& page_table) {
Clear();
- return ParseCapabilities(capabilities, num_capabilities, vm_manager);
+ return ParseCapabilities(capabilities, num_capabilities, page_table);
}
void ProcessCapabilities::InitializeForMetadatalessProcess() {
@@ -105,7 +106,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
std::size_t num_capabilities,
- VMManager& vm_manager) {
+ Memory::PageTable& page_table) {
u32 set_flags = 0;
u32 set_svc_bits = 0;
@@ -119,22 +120,30 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
// The MapPhysical type uses two descriptor flags for its parameters.
// If there's only one, then there's a problem.
if (i >= num_capabilities) {
+ LOG_ERROR(Kernel, "Invalid combination! i={}", i);
return ERR_INVALID_COMBINATION;
}
const auto size_flags = capabilities[i];
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
+ LOG_ERROR(Kernel, "Invalid capability type! size_flags={}", size_flags);
return ERR_INVALID_COMBINATION;
}
- const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager);
+ const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table);
if (result.IsError()) {
+ LOG_ERROR(Kernel, "Failed to map physical flags! descriptor={}, size_flags={}",
+ descriptor, size_flags);
return result;
}
} else {
const auto result =
- ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager);
+ ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table);
if (result.IsError()) {
+ LOG_ERROR(
+ Kernel,
+ "Failed to parse capability flag! set_flags={}, set_svc_bits={}, descriptor={}",
+ set_flags, set_svc_bits, descriptor);
return result;
}
}
@@ -144,7 +153,7 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
}
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
- u32 flag, VMManager& vm_manager) {
+ u32 flag, Memory::PageTable& page_table) {
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
@@ -162,6 +171,9 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
const u32 flag_length = GetFlagBitOffset(type);
const u32 set_flag = 1U << flag_length;
if ((set_flag & set_flags & InitializeOnceMask) != 0) {
+ LOG_ERROR(Kernel,
+ "Attempted to initialize flags that may only be initialized once. set_flags={}",
+ set_flags);
return ERR_INVALID_COMBINATION;
}
set_flags |= set_flag;
@@ -172,7 +184,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
case CapabilityType::Syscall:
return HandleSyscallFlags(set_svc_bits, flag);
case CapabilityType::MapIO:
- return HandleMapIOFlags(flag, vm_manager);
+ return HandleMapIOFlags(flag, page_table);
case CapabilityType::Interrupt:
return HandleInterruptFlags(flag);
case CapabilityType::ProgramType:
@@ -187,6 +199,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
break;
}
+ LOG_ERROR(Kernel, "Invalid capability type! type={}", static_cast<u32>(type));
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
}
@@ -208,23 +221,31 @@ void ProcessCapabilities::Clear() {
ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
if (priority_mask != 0 || core_mask != 0) {
+ LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
+ priority_mask, core_mask);
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
}
const u32 core_num_min = (flags >> 16) & 0xFF;
const u32 core_num_max = (flags >> 24) & 0xFF;
if (core_num_min > core_num_max) {
+ LOG_ERROR(Kernel, "Core min is greater than core max! core_num_min={}, core_num_max={}",
+ core_num_min, core_num_max);
return ERR_INVALID_COMBINATION;
}
const u32 priority_min = (flags >> 10) & 0x3F;
const u32 priority_max = (flags >> 4) & 0x3F;
if (priority_min > priority_max) {
+ LOG_ERROR(Kernel,
+ "Priority min is greater than priority max! priority_min={}, priority_max={}",
+ core_num_min, priority_max);
return ERR_INVALID_COMBINATION;
}
// The switch only has 4 usable cores.
if (core_num_max >= 4) {
+ LOG_ERROR(Kernel, "Invalid max cores specified! core_num_max={}", core_num_max);
return ERR_INVALID_PROCESSOR_ID;
}
@@ -259,6 +280,7 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
}
if (svc_number >= svc_capabilities.size()) {
+ LOG_ERROR(Kernel, "Process svc capability is out of range! svc_number={}", svc_number);
return ERR_OUT_OF_RANGE;
}
@@ -269,12 +291,12 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
}
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
- VMManager& vm_manager) {
+ Memory::PageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}
-ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
+ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, Memory::PageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}
@@ -295,6 +317,8 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
// emulate that, it's sufficient to mark every interrupt as defined.
if (interrupt >= interrupt_capabilities.size()) {
+ LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}",
+ interrupt);
return ERR_OUT_OF_RANGE;
}
@@ -307,6 +331,7 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
const u32 reserved = flags >> 17;
if (reserved != 0) {
+ LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
return ERR_RESERVED_VALUE;
}
@@ -324,6 +349,9 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
const u32 major_version = kernel_version >> 19;
if (major_version != 0 || flags < 0x80000) {
+ LOG_ERROR(Kernel,
+ "Kernel version is non zero or flags are too small! major_version={}, flags={}",
+ major_version, flags);
return ERR_INVALID_CAPABILITY_DESCRIPTOR;
}
@@ -334,6 +362,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
const u32 reserved = flags >> 26;
if (reserved != 0) {
+ LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
return ERR_RESERVED_VALUE;
}
@@ -344,6 +373,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
const u32 reserved = flags >> 19;
if (reserved != 0) {
+ LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
return ERR_RESERVED_VALUE;
}
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
index 5cdd80747..ea9d12c16 100644
--- a/src/core/hle/kernel/process_capability.h
+++ b/src/core/hle/kernel/process_capability.h
@@ -12,7 +12,9 @@ union ResultCode;
namespace Kernel {
-class VMManager;
+namespace Memory {
+class PageTable;
+}
/// The possible types of programs that may be indicated
/// by the program type capability descriptor.
@@ -81,27 +83,27 @@ public:
///
/// @param capabilities The capabilities to parse
/// @param num_capabilities The number of capabilities to parse.
- /// @param vm_manager The memory manager to use for handling any mapping-related
+ /// @param page_table The memory manager to use for handling any mapping-related
/// operations (such as mapping IO memory, etc).
///
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
- VMManager& vm_manager);
+ Memory::PageTable& page_table);
/// Initializes this process capabilities instance for a userland process.
///
/// @param capabilities The capabilities to parse.
/// @param num_capabilities The total number of capabilities to parse.
- /// @param vm_manager The memory manager to use for handling any mapping-related
+ /// @param page_table The memory manager to use for handling any mapping-related
/// operations (such as mapping IO memory, etc).
///
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
- VMManager& vm_manager);
+ Memory::PageTable& page_table);
/// Initializes this process capabilities instance for a process that does not
/// have any metadata to parse.
@@ -181,13 +183,13 @@ private:
///
/// @param capabilities The sequence of capability descriptors to parse.
/// @param num_capabilities The number of descriptors within the given sequence.
- /// @param vm_manager The memory manager that will perform any memory
+ /// @param page_table The memory manager that will perform any memory
/// mapping if necessary.
///
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
///
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
- VMManager& vm_manager);
+ Memory::PageTable& page_table);
/// Attempts to parse a capability descriptor that is only represented by a
/// single flag set.
@@ -196,13 +198,13 @@ private:
/// flags being initialized more than once when they shouldn't be.
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
/// @param flag The flag to attempt to parse.
- /// @param vm_manager The memory manager that will perform any memory
+ /// @param page_table The memory manager that will perform any memory
/// mapping if necessary.
///
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
///
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
- VMManager& vm_manager);
+ Memory::PageTable& page_table);
/// Clears the internal state of this process capability instance. Necessary,
/// to have a sane starting point due to us allowing running executables without
@@ -226,10 +228,10 @@ private:
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
/// Handles flags related to mapping physical memory pages.
- ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager);
+ ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, Memory::PageTable& page_table);
/// Handles flags related to mapping IO pages.
- ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager);
+ ResultCode HandleMapIOFlags(u32 flags, Memory::PageTable& page_table);
/// Handles flags related to the interrupt capability flags.
ResultCode HandleInterruptFlags(u32 flags);
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index 9d3d3a81b..6e286419e 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -4,9 +4,12 @@
#include <algorithm>
#include "common/assert.h"
+#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -23,10 +26,12 @@ void ReadableEvent::Acquire(Thread* thread) {
}
void ReadableEvent::Signal() {
- if (!is_signaled) {
- is_signaled = true;
- SynchronizationObject::Signal();
- };
+ if (is_signaled) {
+ return;
+ }
+
+ is_signaled = true;
+ SynchronizationObject::Signal();
}
void ReadableEvent::Clear() {
@@ -34,7 +39,10 @@ void ReadableEvent::Clear() {
}
ResultCode ReadableEvent::Reset() {
+ SchedulerLock lock(kernel);
if (!is_signaled) {
+ LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}",
+ GetObjectId(), GetTypeName(), GetName());
return ERR_INVALID_STATE;
}
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index b53423462..212e442f4 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -16,26 +16,58 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
+bool ResourceLimit::Reserve(ResourceType resource, s64 amount) {
+ return Reserve(resource, amount, 10000000000);
+}
+
+bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) {
+ const std::size_t index{ResourceTypeToIndex(resource)};
+
+ s64 new_value = current[index] + amount;
+ if (new_value > limit[index] && available[index] + amount <= limit[index]) {
+ // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout
+ new_value = current[index] + amount;
+ }
+
+ if (new_value <= limit[index]) {
+ current[index] = new_value;
+ return true;
+ }
+ return false;
+}
+
+void ResourceLimit::Release(ResourceType resource, u64 amount) {
+ Release(resource, amount, amount);
+}
+
+void ResourceLimit::Release(ResourceType resource, u64 used_amount, u64 available_amount) {
+ const std::size_t index{ResourceTypeToIndex(resource)};
+
+ current[index] -= used_amount;
+ available[index] -= available_amount;
+}
+
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
return std::make_shared<ResourceLimit>(kernel);
}
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
- return values.at(ResourceTypeToIndex(resource));
+ return limit.at(ResourceTypeToIndex(resource)) - current.at(ResourceTypeToIndex(resource));
}
s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
- return limits.at(ResourceTypeToIndex(resource));
+ return limit.at(ResourceTypeToIndex(resource));
}
ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
- const auto index = ResourceTypeToIndex(resource);
-
- if (value < values[index]) {
+ const std::size_t index{ResourceTypeToIndex(resource)};
+ if (current[index] <= value) {
+ limit[index] = value;
+ return RESULT_SUCCESS;
+ } else {
+ LOG_ERROR(Kernel, "Limit value is too large! resource={}, value={}, index={}",
+ static_cast<u32>(resource), value, index);
return ERR_INVALID_STATE;
}
-
- values[index] = value;
- return RESULT_SUCCESS;
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 53b89e621..936cc4d0f 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -51,6 +51,11 @@ public:
return HANDLE_TYPE;
}
+ bool Reserve(ResourceType resource, s64 amount);
+ bool Reserve(ResourceType resource, s64 amount, u64 timeout);
+ void Release(ResourceType resource, u64 amount);
+ void Release(ResourceType resource, u64 used_amount, u64 available_amount);
+
/**
* Gets the current value for the specified resource.
* @param resource Requested resource type
@@ -91,10 +96,9 @@ private:
using ResourceArray =
std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
- /// Maximum values a resource type may reach.
- ResourceArray limits{};
- /// Current resource limit values.
- ResourceArray values{};
+ ResourceArray limit{};
+ ResourceArray current{};
+ ResourceArray available{};
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 1140c72a3..6b7db5372 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -6,16 +6,21 @@
// licensed under GPLv2 or later under exception provided by the author.
#include <algorithm>
+#include <mutex>
#include <set>
#include <unordered_set>
#include <utility>
#include "common/assert.h"
+#include "common/bit_util.h"
+#include "common/fiber.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/cpu_manager.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/time_manager.h"
@@ -27,103 +32,149 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
GlobalScheduler::~GlobalScheduler() = default;
void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
+ std::scoped_lock lock{global_list_guard};
thread_list.push_back(std::move(thread));
}
void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
+ std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
-void GlobalScheduler::UnloadThread(std::size_t core) {
- Scheduler& sched = kernel.Scheduler(core);
- sched.UnloadThread();
-}
-
-void GlobalScheduler::SelectThread(std::size_t core) {
+u32 GlobalScheduler::SelectThreads() {
+ ASSERT(is_locked);
const auto update_thread = [](Thread* thread, Scheduler& sched) {
- if (thread != sched.selected_thread.get()) {
+ std::scoped_lock lock{sched.guard};
+ if (thread != sched.selected_thread_set.get()) {
if (thread == nullptr) {
++sched.idle_selection_count;
}
- sched.selected_thread = SharedFrom(thread);
+ sched.selected_thread_set = SharedFrom(thread);
}
- sched.is_context_switch_pending = sched.selected_thread != sched.current_thread;
+ const bool reschedule_pending =
+ sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
+ sched.is_context_switch_pending = reschedule_pending;
std::atomic_thread_fence(std::memory_order_seq_cst);
+ return reschedule_pending;
};
- Scheduler& sched = kernel.Scheduler(core);
- Thread* current_thread = nullptr;
- // Step 1: Get top thread in schedule queue.
- current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
- if (current_thread) {
- update_thread(current_thread, sched);
- return;
+ if (!is_reselection_pending.load()) {
+ return 0;
}
- // Step 2: Try selecting a suggested thread.
- Thread* winner = nullptr;
- std::set<s32> sug_cores;
- for (auto thread : suggested_queue[core]) {
- s32 this_core = thread->GetProcessorID();
- Thread* thread_on_core = nullptr;
- if (this_core >= 0) {
- thread_on_core = scheduled_queue[this_core].front();
- }
- if (this_core < 0 || thread != thread_on_core) {
- winner = thread;
- break;
+ std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{};
+
+ u32 idle_cores{};
+
+ // Step 1: Get top thread in schedule queue.
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ Thread* top_thread =
+ scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
+ if (top_thread != nullptr) {
+ // TODO(Blinkhawk): Implement Thread Pinning
+ } else {
+ idle_cores |= (1U << core);
}
- sug_cores.insert(this_core);
+ top_threads[core] = top_thread;
}
- // if we got a suggested thread, select it, else do a second pass.
- if (winner && winner->GetPriority() > 2) {
- if (winner->IsRunning()) {
- UnloadThread(static_cast<u32>(winner->GetProcessorID()));
+
+ while (idle_cores != 0) {
+ u32 core_id = Common::CountTrailingZeroes32(idle_cores);
+
+ if (!suggested_queue[core_id].empty()) {
+ std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{};
+ std::size_t num_candidates = 0;
+ auto iter = suggested_queue[core_id].begin();
+ Thread* suggested = nullptr;
+ // Step 2: Try selecting a suggested thread.
+ while (iter != suggested_queue[core_id].end()) {
+ suggested = *iter;
+ iter++;
+ s32 suggested_core_id = suggested->GetProcessorID();
+ Thread* top_thread =
+ suggested_core_id >= 0 ? top_threads[suggested_core_id] : nullptr;
+ if (top_thread != suggested) {
+ if (top_thread != nullptr &&
+ top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) {
+ suggested = nullptr;
+ break;
+ // There's a too high thread to do core migration, cancel
+ }
+ TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested);
+ break;
+ }
+ suggested = nullptr;
+ migration_candidates[num_candidates++] = suggested_core_id;
+ }
+ // Step 3: Select a suggested thread from another core
+ if (suggested == nullptr) {
+ for (std::size_t i = 0; i < num_candidates; i++) {
+ s32 candidate_core = migration_candidates[i];
+ suggested = top_threads[candidate_core];
+ auto it = scheduled_queue[candidate_core].begin();
+ it++;
+ Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr;
+ if (next != nullptr) {
+ TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id),
+ suggested);
+ top_threads[candidate_core] = next;
+ break;
+ } else {
+ suggested = nullptr;
+ }
+ }
+ }
+ top_threads[core_id] = suggested;
}
- TransferToCore(winner->GetPriority(), static_cast<s32>(core), winner);
- update_thread(winner, sched);
- return;
+
+ idle_cores &= ~(1U << core_id);
}
- // Step 3: Select a suggested thread from another core
- for (auto& src_core : sug_cores) {
- auto it = scheduled_queue[src_core].begin();
- it++;
- if (it != scheduled_queue[src_core].end()) {
- Thread* thread_on_core = scheduled_queue[src_core].front();
- Thread* to_change = *it;
- if (thread_on_core->IsRunning() || to_change->IsRunning()) {
- UnloadThread(static_cast<u32>(src_core));
- }
- TransferToCore(thread_on_core->GetPriority(), static_cast<s32>(core), thread_on_core);
- current_thread = thread_on_core;
- break;
+ u32 cores_needing_context_switch{};
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ Scheduler& sched = kernel.Scheduler(core);
+ ASSERT(top_threads[core] == nullptr ||
+ static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
+ if (update_thread(top_threads[core], sched)) {
+ cores_needing_context_switch |= (1U << core);
}
}
- update_thread(current_thread, sched);
+ return cores_needing_context_switch;
}
bool GlobalScheduler::YieldThread(Thread* yielding_thread) {
+ ASSERT(is_locked);
// Note: caller should use critical section, etc.
+ if (!yielding_thread->IsRunnable()) {
+ // Normally this case shouldn't happen except for SetThreadActivity.
+ is_reselection_pending.store(true, std::memory_order_release);
+ return false;
+ }
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
const u32 priority = yielding_thread->GetPriority();
// Yield the thread
- const Thread* const winner = scheduled_queue[core_id].front(priority);
- ASSERT_MSG(yielding_thread == winner, "Thread yielding without being in front");
- scheduled_queue[core_id].yield(priority);
+ Reschedule(priority, core_id, yielding_thread);
+ const Thread* const winner = scheduled_queue[core_id].front();
+ if (kernel.GetCurrentHostThreadID() != core_id) {
+ is_reselection_pending.store(true, std::memory_order_release);
+ }
return AskForReselectionOrMarkRedundant(yielding_thread, winner);
}
bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
+ ASSERT(is_locked);
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
// etc.
+ if (!yielding_thread->IsRunnable()) {
+ // Normally this case shouldn't happen except for SetThreadActivity.
+ is_reselection_pending.store(true, std::memory_order_release);
+ return false;
+ }
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
const u32 priority = yielding_thread->GetPriority();
// Yield the thread
- ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority),
- "Thread yielding without being in front");
- scheduled_queue[core_id].yield(priority);
+ Reschedule(priority, core_id, yielding_thread);
std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads;
for (std::size_t i = 0; i < current_threads.size(); i++) {
@@ -153,21 +204,28 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
if (winner != nullptr) {
if (winner != yielding_thread) {
- if (winner->IsRunning()) {
- UnloadThread(static_cast<u32>(winner->GetProcessorID()));
- }
TransferToCore(winner->GetPriority(), s32(core_id), winner);
}
} else {
winner = next_thread;
}
+ if (kernel.GetCurrentHostThreadID() != core_id) {
+ is_reselection_pending.store(true, std::memory_order_release);
+ }
+
return AskForReselectionOrMarkRedundant(yielding_thread, winner);
}
bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) {
+ ASSERT(is_locked);
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
// etc.
+ if (!yielding_thread->IsRunnable()) {
+ // Normally this case shouldn't happen except for SetThreadActivity.
+ is_reselection_pending.store(true, std::memory_order_release);
+ return false;
+ }
Thread* winner = nullptr;
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
@@ -195,25 +253,31 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread
}
if (winner != nullptr) {
if (winner != yielding_thread) {
- if (winner->IsRunning()) {
- UnloadThread(static_cast<u32>(winner->GetProcessorID()));
- }
TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner);
}
} else {
winner = yielding_thread;
}
+ } else {
+ winner = scheduled_queue[core_id].front();
+ }
+
+ if (kernel.GetCurrentHostThreadID() != core_id) {
+ is_reselection_pending.store(true, std::memory_order_release);
}
return AskForReselectionOrMarkRedundant(yielding_thread, winner);
}
void GlobalScheduler::PreemptThreads() {
+ ASSERT(is_locked);
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
if (scheduled_queue[core_id].size(priority) > 0) {
- scheduled_queue[core_id].front(priority)->IncrementYieldCount();
+ if (scheduled_queue[core_id].size(priority) > 1) {
+ scheduled_queue[core_id].front(priority)->IncrementYieldCount();
+ }
scheduled_queue[core_id].yield(priority);
if (scheduled_queue[core_id].size(priority) > 1) {
scheduled_queue[core_id].front(priority)->IncrementYieldCount();
@@ -247,9 +311,6 @@ void GlobalScheduler::PreemptThreads() {
}
if (winner != nullptr) {
- if (winner->IsRunning()) {
- UnloadThread(static_cast<u32>(winner->GetProcessorID()));
- }
TransferToCore(winner->GetPriority(), s32(core_id), winner);
current_thread =
winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread;
@@ -280,9 +341,6 @@ void GlobalScheduler::PreemptThreads() {
}
if (winner != nullptr) {
- if (winner->IsRunning()) {
- UnloadThread(static_cast<u32>(winner->GetProcessorID()));
- }
TransferToCore(winner->GetPriority(), s32(core_id), winner);
current_thread = winner;
}
@@ -292,34 +350,65 @@ void GlobalScheduler::PreemptThreads() {
}
}
+void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule,
+ Core::EmuThreadHandle global_thread) {
+ u32 current_core = global_thread.host_handle;
+ bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
+ (current_core < Core::Hardware::NUM_CPU_CORES);
+ while (cores_pending_reschedule != 0) {
+ u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule);
+ ASSERT(core < Core::Hardware::NUM_CPU_CORES);
+ if (!must_context_switch || core != current_core) {
+ auto& phys_core = kernel.PhysicalCore(core);
+ phys_core.Interrupt();
+ } else {
+ must_context_switch = true;
+ }
+ cores_pending_reschedule &= ~(1U << core);
+ }
+ if (must_context_switch) {
+ auto& core_scheduler = kernel.CurrentScheduler();
+ kernel.ExitSVCProfile();
+ core_scheduler.TryDoContextSwitch();
+ kernel.EnterSVCProfile();
+ }
+}
+
void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) {
+ ASSERT(is_locked);
suggested_queue[core].add(thread, priority);
}
void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) {
+ ASSERT(is_locked);
suggested_queue[core].remove(thread, priority);
}
void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) {
+ ASSERT(is_locked);
ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
scheduled_queue[core].add(thread, priority);
}
void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) {
+ ASSERT(is_locked);
ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
scheduled_queue[core].add(thread, priority, false);
}
void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) {
+ ASSERT(is_locked);
scheduled_queue[core].remove(thread, priority);
scheduled_queue[core].add(thread, priority);
}
void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) {
+ ASSERT(is_locked);
scheduled_queue[core].remove(thread, priority);
}
void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) {
+ ASSERT(is_locked);
const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT;
const s32 source_core = thread->GetProcessorID();
if (source_core == destination_core || !schedulable) {
@@ -349,6 +438,108 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread,
}
}
+void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
+ if (old_flags == thread->scheduling_state) {
+ return;
+ }
+ ASSERT(is_locked);
+
+ if (old_flags == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ // In this case the thread was running, now it's pausing/exitting
+ if (thread->processor_id >= 0) {
+ Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
+ }
+
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ if (core != static_cast<u32>(thread->processor_id) &&
+ ((thread->affinity_mask >> core) & 1) != 0) {
+ Unsuggest(thread->current_priority, core, thread);
+ }
+ }
+ } else if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ // The thread is now set to running from being stopped
+ if (thread->processor_id >= 0) {
+ Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
+ }
+
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ if (core != static_cast<u32>(thread->processor_id) &&
+ ((thread->affinity_mask >> core) & 1) != 0) {
+ Suggest(thread->current_priority, core, thread);
+ }
+ }
+ }
+
+ SetReselectionPending();
+}
+
+void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) {
+ if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ return;
+ }
+ ASSERT(is_locked);
+ if (thread->processor_id >= 0) {
+ Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread);
+ }
+
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ if (core != static_cast<u32>(thread->processor_id) &&
+ ((thread->affinity_mask >> core) & 1) != 0) {
+ Unsuggest(old_priority, core, thread);
+ }
+ }
+
+ if (thread->processor_id >= 0) {
+ if (thread == kernel.CurrentScheduler().GetCurrentThread()) {
+ SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id),
+ thread);
+ } else {
+ Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
+ }
+ }
+
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ if (core != static_cast<u32>(thread->processor_id) &&
+ ((thread->affinity_mask >> core) & 1) != 0) {
+ Suggest(thread->current_priority, core, thread);
+ }
+ }
+ thread->IncrementYieldCount();
+ SetReselectionPending();
+}
+
+void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask,
+ s32 old_core) {
+ if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable) ||
+ thread->current_priority >= THREADPRIO_COUNT) {
+ return;
+ }
+ ASSERT(is_locked);
+
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ if (((old_affinity_mask >> core) & 1) != 0) {
+ if (core == static_cast<u32>(old_core)) {
+ Unschedule(thread->current_priority, core, thread);
+ } else {
+ Unsuggest(thread->current_priority, core, thread);
+ }
+ }
+ }
+
+ for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
+ if (((thread->affinity_mask >> core) & 1) != 0) {
+ if (core == static_cast<u32>(thread->processor_id)) {
+ Schedule(thread->current_priority, core, thread);
+ } else {
+ Suggest(thread->current_priority, core, thread);
+ }
+ }
+ }
+
+ thread->IncrementYieldCount();
+ SetReselectionPending();
+}
+
void GlobalScheduler::Shutdown() {
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
scheduled_queue[core].clear();
@@ -359,10 +550,12 @@ void GlobalScheduler::Shutdown() {
void GlobalScheduler::Lock() {
Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID();
+ ASSERT(!current_thread.IsInvalid());
if (current_thread == current_owner) {
++scope_lock;
} else {
inner_lock.lock();
+ is_locked = true;
current_owner = current_thread;
ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle());
scope_lock = 1;
@@ -374,17 +567,18 @@ void GlobalScheduler::Unlock() {
ASSERT(scope_lock > 0);
return;
}
- for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- SelectThread(i);
- }
+ u32 cores_pending_reschedule = SelectThreads();
+ Core::EmuThreadHandle leaving_thread = current_owner;
current_owner = Core::EmuThreadHandle::InvalidHandle();
scope_lock = 1;
+ is_locked = false;
inner_lock.unlock();
- // TODO(Blinkhawk): Setup the interrupts and change context on current core.
+ EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread);
}
-Scheduler::Scheduler(Core::System& system, std::size_t core_id)
- : system{system}, core_id{core_id} {}
+Scheduler::Scheduler(Core::System& system, std::size_t core_id) : system(system), core_id(core_id) {
+ switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this);
+}
Scheduler::~Scheduler() = default;
@@ -393,56 +587,122 @@ bool Scheduler::HaveReadyThreads() const {
}
Thread* Scheduler::GetCurrentThread() const {
- return current_thread.get();
+ if (current_thread) {
+ return current_thread.get();
+ }
+ return idle_thread.get();
}
Thread* Scheduler::GetSelectedThread() const {
return selected_thread.get();
}
-void Scheduler::SelectThreads() {
- system.GlobalScheduler().SelectThread(core_id);
-}
-
u64 Scheduler::GetLastContextSwitchTicks() const {
return last_context_switch_time;
}
void Scheduler::TryDoContextSwitch() {
+ auto& phys_core = system.Kernel().CurrentPhysicalCore();
+ if (phys_core.IsInterrupted()) {
+ phys_core.ClearInterrupt();
+ }
+ guard.lock();
if (is_context_switch_pending) {
SwitchContext();
+ } else {
+ guard.unlock();
}
}
-void Scheduler::UnloadThread() {
- Thread* const previous_thread = GetCurrentThread();
- Process* const previous_process = system.Kernel().CurrentProcess();
+void Scheduler::OnThreadStart() {
+ SwitchContextStep2();
+}
- UpdateLastContextSwitchTime(previous_thread, previous_process);
+void Scheduler::Unload() {
+ Thread* thread = current_thread.get();
+ if (thread) {
+ thread->SetContinuousOnSVC(false);
+ thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
+ thread->SetIsRunning(false);
+ if (!thread->IsHLEThread() && !thread->HasExited()) {
+ Core::ARM_Interface& cpu_core = thread->ArmInterface();
+ cpu_core.SaveContext(thread->GetContext32());
+ cpu_core.SaveContext(thread->GetContext64());
+ // Save the TPIDR_EL0 system register in case it was modified.
+ thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
+ }
+ thread->context_guard.unlock();
+ }
+}
- // Save context for previous thread
- if (previous_thread) {
- system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
- system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
+void Scheduler::Reload() {
+ Thread* thread = current_thread.get();
+ if (thread) {
+ ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
+ "Thread must be runnable.");
+
+ // Cancel any outstanding wakeup events for this thread
+ thread->SetIsRunning(true);
+ thread->SetWasRunning(false);
+ thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
- if (previous_thread->GetStatus() == ThreadStatus::Running) {
- // This is only the case when a reschedule is triggered without the current thread
- // yielding execution (i.e. an event triggered, system core time-sliced, etc)
- previous_thread->SetStatus(ThreadStatus::Ready);
+ auto* const thread_owner_process = thread->GetOwnerProcess();
+ if (thread_owner_process != nullptr) {
+ system.Kernel().MakeCurrentProcess(thread_owner_process);
+ }
+ if (!thread->IsHLEThread()) {
+ Core::ARM_Interface& cpu_core = thread->ArmInterface();
+ cpu_core.LoadContext(thread->GetContext32());
+ cpu_core.LoadContext(thread->GetContext64());
+ cpu_core.SetTlsAddress(thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
+ cpu_core.ChangeProcessorID(this->core_id);
+ cpu_core.ClearExclusiveState();
}
- previous_thread->SetIsRunning(false);
}
- current_thread = nullptr;
+}
+
+void Scheduler::SwitchContextStep2() {
+ // Load context of new thread
+ if (selected_thread) {
+ ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
+ "Thread must be runnable.");
+
+ // Cancel any outstanding wakeup events for this thread
+ selected_thread->SetIsRunning(true);
+ selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
+ selected_thread->SetWasRunning(false);
+
+ auto* const thread_owner_process = current_thread->GetOwnerProcess();
+ if (thread_owner_process != nullptr) {
+ system.Kernel().MakeCurrentProcess(thread_owner_process);
+ }
+ if (!selected_thread->IsHLEThread()) {
+ Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
+ cpu_core.LoadContext(selected_thread->GetContext32());
+ cpu_core.LoadContext(selected_thread->GetContext64());
+ cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
+ cpu_core.ChangeProcessorID(this->core_id);
+ cpu_core.ClearExclusiveState();
+ }
+ }
+
+ TryDoContextSwitch();
}
void Scheduler::SwitchContext() {
- Thread* const previous_thread = GetCurrentThread();
- Thread* const new_thread = GetSelectedThread();
+ current_thread_prev = current_thread;
+ selected_thread = selected_thread_set;
+ Thread* previous_thread = current_thread_prev.get();
+ Thread* new_thread = selected_thread.get();
+ current_thread = selected_thread;
is_context_switch_pending = false;
+
if (new_thread == previous_thread) {
+ guard.unlock();
return;
}
@@ -452,51 +712,80 @@ void Scheduler::SwitchContext() {
// Save context for previous thread
if (previous_thread) {
- system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
- system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
-
- if (previous_thread->GetStatus() == ThreadStatus::Running) {
- // This is only the case when a reschedule is triggered without the current thread
- // yielding execution (i.e. an event triggered, system core time-sliced, etc)
- previous_thread->SetStatus(ThreadStatus::Ready);
+ if (new_thread != nullptr && new_thread->IsSuspendThread()) {
+ previous_thread->SetWasRunning(true);
}
+ previous_thread->SetContinuousOnSVC(false);
+ previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
previous_thread->SetIsRunning(false);
+ if (!previous_thread->IsHLEThread() && !previous_thread->HasExited()) {
+ Core::ARM_Interface& cpu_core = previous_thread->ArmInterface();
+ cpu_core.SaveContext(previous_thread->GetContext32());
+ cpu_core.SaveContext(previous_thread->GetContext64());
+ // Save the TPIDR_EL0 system register in case it was modified.
+ previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
+ }
+ previous_thread->context_guard.unlock();
}
- // Load context of new thread
- if (new_thread) {
- ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id),
- "Thread must be assigned to this core.");
- ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
- "Thread must be ready to become running.");
+ std::shared_ptr<Common::Fiber>* old_context;
+ if (previous_thread != nullptr) {
+ old_context = &previous_thread->GetHostContext();
+ } else {
+ old_context = &idle_thread->GetHostContext();
+ }
+ guard.unlock();
- // Cancel any outstanding wakeup events for this thread
- new_thread->CancelWakeupTimer();
- current_thread = SharedFrom(new_thread);
- new_thread->SetStatus(ThreadStatus::Running);
- new_thread->SetIsRunning(true);
+ Common::Fiber::YieldTo(*old_context, switch_fiber);
+ /// When a thread wakes up, the scheduler may have changed to other in another core.
+ auto& next_scheduler = system.Kernel().CurrentScheduler();
+ next_scheduler.SwitchContextStep2();
+}
- auto* const thread_owner_process = current_thread->GetOwnerProcess();
- if (previous_process != thread_owner_process) {
- system.Kernel().MakeCurrentProcess(thread_owner_process);
- }
+void Scheduler::OnSwitch(void* this_scheduler) {
+ Scheduler* sched = static_cast<Scheduler*>(this_scheduler);
+ sched->SwitchToCurrent();
+}
- system.ArmInterface(core_id).LoadContext(new_thread->GetContext32());
- system.ArmInterface(core_id).LoadContext(new_thread->GetContext64());
- system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress());
- system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
- } else {
- current_thread = nullptr;
- // Note: We do not reset the current process and current page table when idling because
- // technically we haven't changed processes, our threads are just paused.
+void Scheduler::SwitchToCurrent() {
+ while (true) {
+ {
+ std::scoped_lock lock{guard};
+ selected_thread = selected_thread_set;
+ current_thread = selected_thread;
+ is_context_switch_pending = false;
+ }
+ const auto is_switch_pending = [this] {
+ std::scoped_lock lock{guard};
+ return is_context_switch_pending;
+ };
+ do {
+ if (current_thread != nullptr && !current_thread->IsHLEThread()) {
+ current_thread->context_guard.lock();
+ if (!current_thread->IsRunnable()) {
+ current_thread->context_guard.unlock();
+ break;
+ }
+ if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) {
+ current_thread->context_guard.unlock();
+ break;
+ }
+ }
+ std::shared_ptr<Common::Fiber>* next_context;
+ if (current_thread != nullptr) {
+ next_context = &current_thread->GetHostContext();
+ } else {
+ next_context = &idle_thread->GetHostContext();
+ }
+ Common::Fiber::YieldTo(switch_fiber, *next_context);
+ } while (!is_switch_pending());
}
}
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks();
+ const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
@@ -510,6 +799,16 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
last_context_switch_time = most_recent_switch_ticks;
}
+void Scheduler::Initialize() {
+ std::string name = "Idle Thread Id:" + std::to_string(core_id);
+ std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
+ void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
+ ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
+ auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
+ nullptr, std::move(init_func), init_func_parameter);
+ idle_thread = std::move(thread_res).Unwrap();
+}
+
void Scheduler::Shutdown() {
current_thread = nullptr;
selected_thread = nullptr;
@@ -538,4 +837,13 @@ SchedulerLockAndSleep::~SchedulerLockAndSleep() {
time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
}
+void SchedulerLockAndSleep::Release() {
+ if (sleep_cancelled) {
+ return;
+ }
+ auto& time_manager = kernel.TimeManager();
+ time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
+ sleep_cancelled = true;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 07df33f9c..b6f04dcea 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -11,9 +11,14 @@
#include "common/common_types.h"
#include "common/multi_level_queue.h"
+#include "common/spin_lock.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/thread.h"
+namespace Common {
+class Fiber;
+}
+
namespace Core {
class ARM_Interface;
class System;
@@ -41,41 +46,17 @@ public:
return thread_list;
}
- /**
- * Add a thread to the suggested queue of a cpu core. Suggested threads may be
- * picked if no thread is scheduled to run on the core.
- */
- void Suggest(u32 priority, std::size_t core, Thread* thread);
-
- /**
- * Remove a thread to the suggested queue of a cpu core. Suggested threads may be
- * picked if no thread is scheduled to run on the core.
- */
- void Unsuggest(u32 priority, std::size_t core, Thread* thread);
-
- /**
- * Add a thread to the scheduling queue of a cpu core. The thread is added at the
- * back the queue in its priority level.
- */
- void Schedule(u32 priority, std::size_t core, Thread* thread);
-
- /**
- * Add a thread to the scheduling queue of a cpu core. The thread is added at the
- * front the queue in its priority level.
- */
- void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
+ /// Notify the scheduler a thread's status has changed.
+ void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags);
- /// Reschedule an already scheduled thread based on a new priority
- void Reschedule(u32 priority, std::size_t core, Thread* thread);
-
- /// Unschedules a thread.
- void Unschedule(u32 priority, std::size_t core, Thread* thread);
+ /// Notify the scheduler a thread's priority has changed.
+ void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority);
- /// Selects a core and forces it to unload its current thread's context
- void UnloadThread(std::size_t core);
+ /// Notify the scheduler a thread's core and/or affinity mask has changed.
+ void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core);
/**
- * Takes care of selecting the new scheduled thread in three steps:
+ * Takes care of selecting the new scheduled threads in three steps:
*
* 1. First a thread is selected from the top of the priority queue. If no thread
* is obtained then we move to step two, else we are done.
@@ -85,8 +66,10 @@ public:
*
* 3. Third is no suggested thread is found, we do a second pass and pick a running
* thread in another core and swap it with its current thread.
+ *
+ * returns the cores needing scheduling.
*/
- void SelectThread(std::size_t core);
+ u32 SelectThreads();
bool HaveReadyThreads(std::size_t core_id) const {
return !scheduled_queue[core_id].empty();
@@ -149,6 +132,40 @@ private:
/// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling
/// and reschedules current core if needed.
void Unlock();
+
+ void EnableInterruptAndSchedule(u32 cores_pending_reschedule,
+ Core::EmuThreadHandle global_thread);
+
+ /**
+ * Add a thread to the suggested queue of a cpu core. Suggested threads may be
+ * picked if no thread is scheduled to run on the core.
+ */
+ void Suggest(u32 priority, std::size_t core, Thread* thread);
+
+ /**
+ * Remove a thread to the suggested queue of a cpu core. Suggested threads may be
+ * picked if no thread is scheduled to run on the core.
+ */
+ void Unsuggest(u32 priority, std::size_t core, Thread* thread);
+
+ /**
+ * Add a thread to the scheduling queue of a cpu core. The thread is added at the
+ * back the queue in its priority level.
+ */
+ void Schedule(u32 priority, std::size_t core, Thread* thread);
+
+ /**
+ * Add a thread to the scheduling queue of a cpu core. The thread is added at the
+ * front the queue in its priority level.
+ */
+ void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
+
+ /// Reschedule an already scheduled thread based on a new priority
+ void Reschedule(u32 priority, std::size_t core, Thread* thread);
+
+ /// Unschedules a thread.
+ void Unschedule(u32 priority, std::size_t core, Thread* thread);
+
/**
* Transfers a thread into an specific core. If the destination_core is -1
* it will be unscheduled from its source code and added into its suggested
@@ -170,10 +187,13 @@ private:
std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62};
/// Scheduler lock mechanisms.
- std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock
+ bool is_locked{};
+ std::mutex inner_lock;
std::atomic<s64> scope_lock{};
Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
+ Common::SpinLock global_list_guard{};
+
/// Lists all thread ids that aren't deleted/etc.
std::vector<std::shared_ptr<Thread>> thread_list;
KernelCore& kernel;
@@ -190,11 +210,11 @@ public:
/// Reschedules to the next available thread (call after current thread is suspended)
void TryDoContextSwitch();
- /// Unloads currently running thread
- void UnloadThread();
-
- /// Select the threads in top of the scheduling multilist.
- void SelectThreads();
+ /// The next two are for SingleCore Only.
+ /// Unload current thread before preempting core.
+ void Unload();
+ /// Reload current thread after core preemption.
+ void Reload();
/// Gets the current running thread
Thread* GetCurrentThread() const;
@@ -209,15 +229,30 @@ public:
return is_context_switch_pending;
}
+ void Initialize();
+
/// Shutdowns the scheduler.
void Shutdown();
+ void OnThreadStart();
+
+ std::shared_ptr<Common::Fiber>& ControlContext() {
+ return switch_fiber;
+ }
+
+ const std::shared_ptr<Common::Fiber>& ControlContext() const {
+ return switch_fiber;
+ }
+
private:
friend class GlobalScheduler;
/// Switches the CPU's active thread context to that of the specified thread
void SwitchContext();
+ /// When a thread wakes up, it must run this through it's new scheduler
+ void SwitchContextStep2();
+
/**
* Called on every context switch to update the internal timestamp
* This also updates the running time ticks for the given thread and
@@ -231,20 +266,30 @@ private:
*/
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
+ static void OnSwitch(void* this_scheduler);
+ void SwitchToCurrent();
+
std::shared_ptr<Thread> current_thread = nullptr;
std::shared_ptr<Thread> selected_thread = nullptr;
+ std::shared_ptr<Thread> current_thread_prev = nullptr;
+ std::shared_ptr<Thread> selected_thread_set = nullptr;
+ std::shared_ptr<Thread> idle_thread = nullptr;
+
+ std::shared_ptr<Common::Fiber> switch_fiber = nullptr;
Core::System& system;
u64 last_context_switch_time = 0;
u64 idle_selection_count = 0;
const std::size_t core_id;
+ Common::SpinLock guard{};
+
bool is_context_switch_pending = false;
};
class SchedulerLock {
public:
- explicit SchedulerLock(KernelCore& kernel);
+ [[nodiscard]] explicit SchedulerLock(KernelCore& kernel);
~SchedulerLock();
protected:
@@ -261,6 +306,8 @@ public:
sleep_cancelled = true;
}
+ void Release();
+
private:
Handle& event_handle;
Thread* time_task;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 4604e35c5..8c19f2534 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -8,7 +8,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
@@ -17,6 +16,7 @@
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
@@ -32,8 +32,10 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
std::string name) {
std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
- session->request_event = Core::Timing::CreateEvent(
- name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); });
+ session->request_event =
+ Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
+ session->CompleteSyncRequest();
+ });
session->name = std::move(name);
session->parent = std::move(parent);
@@ -134,10 +136,11 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
return RESULT_SUCCESS;
}
-ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) {
+ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
+ Core::Memory::Memory& memory) {
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
- std::shared_ptr<Kernel::HLERequestContext> context{
- std::make_shared<Kernel::HLERequestContext>(SharedFrom(this), std::move(thread))};
+ auto context =
+ std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
request_queue.Push(std::move(context));
@@ -167,9 +170,12 @@ ResultCode ServerSession::CompleteSyncRequest() {
}
// Some service requests require the thread to block
- if (!context.IsThreadWaiting()) {
- context.GetThread().ResumeFromWait();
- context.GetThread().SetWaitSynchronizationResult(result);
+ {
+ SchedulerLock lock(kernel);
+ if (!context.IsThreadWaiting()) {
+ context.GetThread().ResumeFromWait();
+ context.GetThread().SetSynchronizationResults(nullptr, result);
+ }
}
request_queue.Pop();
@@ -178,9 +184,12 @@ ResultCode ServerSession::CompleteSyncRequest() {
}
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
- Memory::Memory& memory) {
- Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {});
- return QueueSyncRequest(std::move(thread), memory);
+ Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
+ const ResultCode result = QueueSyncRequest(std::move(thread), memory);
+ const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
+ core_timing.ScheduleEvent(delay, request_event, {});
+ return result;
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 77e4f6721..d23e9ec68 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -13,13 +13,14 @@
#include "core/hle/kernel/synchronization_object.h"
#include "core/hle/result.h"
-namespace Memory {
+namespace Core::Memory {
class Memory;
}
namespace Core::Timing {
+class CoreTiming;
struct EventType;
-}
+} // namespace Core::Timing
namespace Kernel {
@@ -87,12 +88,14 @@ public:
/**
* Handle a sync request from the emulated application.
*
- * @param thread Thread that initiated the request.
- * @param memory Memory context to handle the sync request under.
+ * @param thread Thread that initiated the request.
+ * @param memory Memory context to handle the sync request under.
+ * @param core_timing Core timing context to schedule the request event under.
*
* @returns ResultCode from the operation.
*/
- ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
+ ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
bool ShouldWait(const Thread* thread) const override;
@@ -126,7 +129,7 @@ public:
private:
/// Queues a sync request from the emulated application.
- ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
+ ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
/// Completes a sync request from the emulated application.
ResultCode CompleteSyncRequest();
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index afb2e3fc2..0cd467110 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -2,149 +2,56 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <utility>
-
#include "common/assert.h"
-#include "common/logging/log.h"
-#include "core/hle/kernel/errors.h"
+#include "core/core.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/shared_memory.h"
namespace Kernel {
-SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {}
-SharedMemory::~SharedMemory() = default;
-
-std::shared_ptr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process,
- u64 size, MemoryPermission permissions,
- MemoryPermission other_permissions,
- VAddr address, MemoryRegion region,
- std::string name) {
- std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel);
-
- shared_memory->owner_process = owner_process;
- shared_memory->name = std::move(name);
- shared_memory->size = size;
- shared_memory->permissions = permissions;
- shared_memory->other_permissions = other_permissions;
-
- if (address == 0) {
- shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size);
- shared_memory->backing_block_offset = 0;
-
- // Refresh the address mappings for the current process.
- if (kernel.CurrentProcess() != nullptr) {
- kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
- shared_memory->backing_block.get());
- }
- } else {
- const auto& vm_manager = shared_memory->owner_process->VMManager();
+SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
+ : Object{kernel}, device_memory{device_memory} {}
- // The memory is already available and mapped in the owner process.
- const auto vma = vm_manager.FindVMA(address);
- ASSERT_MSG(vm_manager.IsValidHandle(vma), "Invalid memory address");
- ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
-
- // The returned VMA might be a bigger one encompassing the desired address.
- const auto vma_offset = address - vma->first;
- ASSERT_MSG(vma_offset + size <= vma->second.size,
- "Shared memory exceeds bounds of mapped block");
-
- shared_memory->backing_block = vma->second.backing_block;
- shared_memory->backing_block_offset = vma->second.offset + vma_offset;
- }
-
- shared_memory->base_address = address;
+SharedMemory::~SharedMemory() = default;
- return shared_memory;
-}
+std::shared_ptr<SharedMemory> SharedMemory::Create(
+ KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
+ Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
+ Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
+ std::string name) {
-std::shared_ptr<SharedMemory> SharedMemory::CreateForApplet(
- KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
- u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
- std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel);
+ std::shared_ptr<SharedMemory> shared_memory{
+ std::make_shared<SharedMemory>(kernel, device_memory)};
- shared_memory->owner_process = nullptr;
- shared_memory->name = std::move(name);
+ shared_memory->owner_process = owner_process;
+ shared_memory->page_list = std::move(page_list);
+ shared_memory->owner_permission = owner_permission;
+ shared_memory->user_permission = user_permission;
+ shared_memory->physical_address = physical_address;
shared_memory->size = size;
- shared_memory->permissions = permissions;
- shared_memory->other_permissions = other_permissions;
- shared_memory->backing_block = std::move(heap_block);
- shared_memory->backing_block_offset = offset;
- shared_memory->base_address =
- kernel.CurrentProcess()->VMManager().GetHeapRegionBaseAddress() + offset;
+ shared_memory->name = name;
return shared_memory;
}
-ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions,
- MemoryPermission 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_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_MEMORY_PERMISSIONS;
- }
+ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
+ Memory::MemoryPermission permissions) {
+ const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize};
- // Error out if the provided permissions are not compatible with what the creator process needs.
- if (other_permissions != MemoryPermission::DontCare &&
- static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
- LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
- GetObjectId(), address, name);
- return ERR_INVALID_MEMORY_PERMISSIONS;
+ if (page_list.GetNumPages() != page_count) {
+ UNIMPLEMENTED_MSG("Page count does not match");
}
- VAddr target_address = address;
+ const Memory::MemoryPermission expected =
+ &target_process == owner_process ? owner_permission : user_permission;
- // Map the memory block into the target process
- auto result = target_process.VMManager().MapMemoryBlock(
- target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
- if (result.Failed()) {
- LOG_ERROR(
- Kernel,
- "cannot map id={}, target_address=0x{:X} name={}, error mapping to virtual memory",
- GetObjectId(), target_address, name);
- return result.Code();
+ if (permissions != expected) {
+ UNIMPLEMENTED_MSG("Permission does not match");
}
- return target_process.VMManager().ReprotectRange(target_address, size,
- ConvertPermissions(permissions));
-}
-
-ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) {
- if (unmap_size != size) {
- LOG_ERROR(Kernel,
- "Invalid size passed to Unmap. Size must be equal to the size of the "
- "memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}",
- size, unmap_size);
- return ERR_INVALID_SIZE;
- }
-
- // TODO(Subv): Verify what happens if the application tries to unmap an address that is not
- // mapped to a SharedMemory.
- return target_process.VMManager().UnmapRange(address, size);
-}
-
-VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
- u32 masked_permissions =
- static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute);
- return static_cast<VMAPermission>(masked_permissions);
-}
-
-u8* SharedMemory::GetPointer(std::size_t offset) {
- return backing_block->data() + backing_block_offset + offset;
-}
-
-const u8* SharedMemory::GetPointer(std::size_t offset) const {
- return backing_block->data() + backing_block_offset + offset;
+ return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared,
+ permissions);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 014951d82..0ef87235c 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -8,8 +8,10 @@
#include <string>
#include "common/common_types.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/memory/memory_block.h"
+#include "core/hle/kernel/memory/page_linked_list.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
@@ -17,63 +19,21 @@ namespace Kernel {
class KernelCore;
-/// Permissions for mapped shared memory blocks
-enum class MemoryPermission : u32 {
- None = 0,
- Read = (1u << 0),
- Write = (1u << 1),
- ReadWrite = (Read | Write),
- Execute = (1u << 2),
- ReadExecute = (Read | Execute),
- WriteExecute = (Write | Execute),
- ReadWriteExecute = (Read | Write | Execute),
- DontCare = (1u << 28)
-};
-
class SharedMemory final : public Object {
public:
- explicit SharedMemory(KernelCore& kernel);
+ explicit SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
~SharedMemory() override;
- /**
- * Creates a shared memory object.
- * @param kernel The kernel instance to create a shared memory instance under.
- * @param owner_process Process that created this shared memory object.
- * @param size Size of the memory block. Must be page-aligned.
- * @param permissions Permission restrictions applied to the process which created the block.
- * @param other_permissions Permission restrictions applied to other processes mapping the
- * block.
- * @param address The address from which to map the Shared Memory.
- * @param region If the address is 0, the shared memory will be allocated in this region of the
- * linear heap.
- * @param name Optional object name, used for debugging purposes.
- */
- static std::shared_ptr<SharedMemory> Create(KernelCore& kernel, Process* owner_process,
- u64 size, MemoryPermission permissions,
- MemoryPermission other_permissions,
- VAddr address = 0,
- MemoryRegion region = MemoryRegion::BASE,
- std::string name = "Unknown");
-
- /**
- * Creates a shared memory object from a block of memory managed by an HLE applet.
- * @param kernel The kernel instance to create a shared memory instance under.
- * @param heap_block Heap block of the HLE applet.
- * @param offset The offset into the heap block that the SharedMemory will map.
- * @param size Size of the memory block. Must be page-aligned.
- * @param permissions Permission restrictions applied to the process which created the block.
- * @param other_permissions Permission restrictions applied to other processes mapping the
- * block.
- * @param name Optional object name, used for debugging purposes.
- */
- static std::shared_ptr<SharedMemory> CreateForApplet(
- KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
- u64 size, MemoryPermission permissions, MemoryPermission other_permissions,
- std::string name = "Unknown Applet");
+ static std::shared_ptr<SharedMemory> Create(
+ KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
+ Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
+ Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
+ std::string name);
std::string GetTypeName() const override {
return "SharedMemory";
}
+
std::string GetName() const override {
return name;
}
@@ -83,71 +43,42 @@ public:
return HANDLE_TYPE;
}
- /// Gets the size of the underlying memory block in bytes.
- u64 GetSize() const {
- return size;
- }
-
- /**
- * Converts the specified MemoryPermission into the equivalent VMAPermission.
- * @param permission The MemoryPermission to convert.
- */
- static VMAPermission ConvertPermissions(MemoryPermission permission);
-
/**
* Maps a shared memory block to an address in the target process' address space
- * @param target_process Process on which to map the memory block.
+ * @param target_process Process on which to map the memory block
* @param address Address in system memory to map shared memory block to
+ * @param size Size of the shared memory block to map
* @param permissions Memory block map permissions (specified by SVC field)
- * @param other_permissions Memory block map other permissions (specified by SVC field)
- */
- ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions,
- MemoryPermission other_permissions);
-
- /**
- * Unmaps a shared memory block from the specified address in system memory
- *
- * @param target_process Process from which to unmap the memory block.
- * @param address Address in system memory where the shared memory block is mapped.
- * @param unmap_size The amount of bytes to unmap from this shared memory instance.
- *
- * @return Result code of the unmap operation
- *
- * @pre The given size to unmap must be the same size as the amount of memory managed by
- * the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned.
*/
- ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size);
+ ResultCode Map(Process& target_process, VAddr address, std::size_t size,
+ Memory::MemoryPermission permissions);
/**
* Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
* @return A pointer to the shared memory block from the specified offset
*/
- u8* GetPointer(std::size_t offset = 0);
+ u8* GetPointer(std::size_t offset = 0) {
+ return device_memory.GetPointer(physical_address + offset);
+ }
/**
- * Gets a constant pointer to the shared memory block
+ * Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
- * @return A constant pointer to the shared memory block from the specified offset
+ * @return A pointer to the shared memory block from the specified offset
*/
- const u8* GetPointer(std::size_t offset = 0) const;
+ const u8* GetPointer(std::size_t offset = 0) const {
+ return device_memory.GetPointer(physical_address + offset);
+ }
private:
- /// Backing memory for this shared memory block.
- std::shared_ptr<PhysicalMemory> backing_block;
- /// Offset into the backing block for this shared memory.
- std::size_t backing_block_offset = 0;
- /// Size of the memory block. Page-aligned.
- u64 size = 0;
- /// Permission restrictions applied to the process which created the block.
- MemoryPermission permissions{};
- /// Permission restrictions applied to other processes mapping the block.
- MemoryPermission other_permissions{};
- /// Process that created this shared memory block.
- Process* owner_process;
- /// Address of shared memory block in the owner process if specified.
- VAddr base_address = 0;
- /// Name of shared memory object.
+ Core::DeviceMemory& device_memory;
+ Process* owner_process{};
+ Memory::PageLinkedList page_list;
+ Memory::MemoryPermission owner_permission{};
+ Memory::MemoryPermission user_permission{};
+ PAddr physical_address{};
+ std::size_t size{};
std::string name;
};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4ffc113c2..e3b770d66 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -10,30 +10,36 @@
#include "common/alignment.h"
#include "common/assert.h"
+#include "common/fiber.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
-#include "core/core_manager.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
+#include "core/cpu_manager.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory/memory_block.h"
+#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_types.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/synchronization.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
@@ -42,7 +48,7 @@
#include "core/memory.h"
#include "core/reporter.h"
-namespace Kernel {
+namespace Kernel::Svc {
namespace {
// Checks if address + size is greater than the given address
@@ -52,14 +58,11 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
return address + size > address;
}
-// 8 GiB
-constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
-
// 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) {
+ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr dst_addr,
+ VAddr src_addr, u64 size) {
if (!Common::Is4KBAligned(dst_addr)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
return ERR_INVALID_ADDRESS;
@@ -93,36 +96,33 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
return ERR_INVALID_ADDRESS_STATE;
}
- if (!vm_manager.IsWithinAddressSpace(src_addr, size)) {
+ if (!manager.IsInsideAddressSpace(src_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
- if (!vm_manager.IsWithinStackRegion(dst_addr, size)) {
+ if (manager.IsOutsideStackRegion(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
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) {
+ if (manager.IsInsideHeapRegion(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination does not fit within the heap region, addr=0x{:016X}, "
- "size=0x{:016X}, end_addr=0x{:016X}",
- dst_addr, size, dst_end_address);
+ "size=0x{:016X}",
+ dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
- if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
- vm_manager.GetMapRegionEndAddress() > dst_addr) {
+ if (manager.IsInsideAliasRegion(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination does not fit within the map region, addr=0x{:016X}, "
- "size=0x{:016X}, end_addr=0x{:016X}",
- dst_addr, size, dst_end_address);
+ "size=0x{:016X}",
+ dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
@@ -136,6 +136,7 @@ enum class ResourceLimitValueType {
ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
u32 resource_type, ResourceLimitValueType value_type) {
+ std::lock_guard lock{HLE::g_hle_lock};
const auto type = static_cast<ResourceType>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
@@ -163,6 +164,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_
/// Set the process heap to a given Size. It can both extend and shrink the heap.
static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
// Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
@@ -177,13 +179,10 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s
return ERR_INVALID_SIZE;
}
- auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
- const auto alloc_result = vm_manager.SetHeapSize(heap_size);
- if (alloc_result.Failed()) {
- return alloc_result.Code();
- }
+ auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
+
+ CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size));
- *heap_addr = *alloc_result;
return RESULT_SUCCESS;
}
@@ -194,65 +193,9 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s
return result;
}
-static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) {
- LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
-
- if (!Common::Is4KBAligned(addr)) {
- LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
- return ERR_INVALID_ADDRESS;
- }
-
- if (size == 0) {
- LOG_ERROR(Kernel_SVC, "Size is 0");
- return ERR_INVALID_SIZE;
- }
-
- if (!Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
- return ERR_INVALID_SIZE;
- }
-
- if (!IsValidAddressRange(addr, size)) {
- LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
- addr, size);
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- const auto permission = static_cast<MemoryPermission>(prot);
- if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
- permission != MemoryPermission::ReadWrite) {
- LOG_ERROR(Kernel_SVC, "Invalid memory permission specified, Got memory permission=0x{:08X}",
- static_cast<u32>(permission));
- return ERR_INVALID_MEMORY_PERMISSIONS;
- }
-
- auto* const current_process = system.Kernel().CurrentProcess();
- auto& vm_manager = current_process->VMManager();
-
- if (!vm_manager.IsWithinAddressSpace(addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
- size);
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
- if (!vm_manager.IsValidHandle(iter)) {
- LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr);
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
- // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
- // make sense to allow changing permissions on kernel memory itself, etc).
-
- const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
-
- return vm_manager.ReprotectRange(addr, size, converted_permissions);
-}
-
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
u32 attribute) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attribute);
@@ -274,72 +217,73 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
return ERR_INVALID_ADDRESS_STATE;
}
- const auto mem_attribute = static_cast<MemoryAttribute>(attribute);
- const auto mem_mask = static_cast<MemoryAttribute>(mask);
- const auto attribute_with_mask = mem_attribute | mem_mask;
-
- if (attribute_with_mask != mem_mask) {
+ const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)};
+ if (attributes != static_cast<Memory::MemoryAttribute>(mask) ||
+ (attributes | Memory::MemoryAttribute::Uncached) != Memory::MemoryAttribute::Uncached) {
LOG_ERROR(Kernel_SVC,
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
attribute, mask);
return ERR_INVALID_COMBINATION;
}
- if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
- LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8).");
- return ERR_INVALID_COMBINATION;
- }
+ auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
- if (!vm_manager.IsWithinAddressSpace(address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Given address (0x{:016X}) is outside the bounds of the address space.", address);
- return ERR_INVALID_ADDRESS_STATE;
- }
+ return page_table.SetMemoryAttribute(address, size, static_cast<Memory::MemoryAttribute>(mask),
+ static_cast<Memory::MemoryAttribute>(attribute));
+}
- return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute);
+static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
+ u32 attribute) {
+ return SetMemoryAttribute(system, static_cast<VAddr>(address), static_cast<std::size_t>(size),
+ mask, attribute);
}
/// Maps a memory range into a different range.
static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
- const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
+ auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- if (result.IsError()) {
+ if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ result.IsError()) {
return result;
}
- return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
+ return page_table.Map(dst_addr, src_addr, size);
+}
+
+static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
+ return MapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr),
+ static_cast<std::size_t>(size));
}
/// Unmaps a region that was previously mapped with svcMapMemory
static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
- const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
+ auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- if (result.IsError()) {
+ if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ result.IsError()) {
return result;
}
- const auto unmap_res = vm_manager.UnmapRange(dst_addr, size);
-
- // Reprotect the source mapping on success
- if (unmap_res.IsSuccess()) {
- ASSERT(vm_manager.ReprotectRange(src_addr, size, VMAPermission::ReadWrite).IsSuccess());
- }
+ return page_table.Unmap(dst_addr, src_addr, size);
+}
- return unmap_res;
+static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
+ return UnmapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr),
+ static_cast<std::size_t>(size));
}
/// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
VAddr port_name_address) {
+ std::lock_guard lock{HLE::g_hle_lock};
auto& memory = system.Memory();
if (!memory.IsValidVirtualAddress(port_name_address)) {
@@ -367,6 +311,8 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
return ERR_NOT_FOUND;
}
+ ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1));
+
auto client_port = it->second;
std::shared_ptr<ClientSession> client_session;
@@ -396,11 +342,30 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
auto thread = system.CurrentScheduler().GetCurrentThread();
- thread->InvalidateWakeupCallback();
- thread->SetStatus(ThreadStatus::WaitIPC);
- system.PrepareReschedule(thread->GetProcessorID());
+ {
+ SchedulerLock lock(system.Kernel());
+ thread->InvalidateHLECallback();
+ thread->SetStatus(ThreadStatus::WaitIPC);
+ session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
+ }
+
+ if (thread->HasHLECallback()) {
+ Handle event_handle = thread->GetHLETimeEvent();
+ if (event_handle != InvalidHandle) {
+ auto& time_manager = system.Kernel().TimeManager();
+ time_manager.UnscheduleTimeEvent(event_handle);
+ }
+
+ {
+ SchedulerLock lock(system.Kernel());
+ auto* sync_object = thread->GetHLESyncObject();
+ sync_object->RemoveWaitingThread(SharedFrom(thread));
+ }
+
+ thread->InvokeHLECallback(SharedFrom(thread));
+ }
- return session->SendSyncRequest(SharedFrom(thread), system.Memory());
+ return thread->GetSignalingResult();
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -462,6 +427,15 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
return ERR_INVALID_HANDLE;
}
+static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high,
+ Handle handle) {
+ u64 process_id{};
+ const auto result = GetProcessId(system, &process_id, handle);
+ *process_id_low = static_cast<u32>(process_id);
+ *process_id_high = static_cast<u32>(process_id >> 32);
+ return result;
+}
+
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
u64 handle_count, s64 nano_seconds) {
@@ -484,9 +458,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
return ERR_OUT_OF_RANGE;
}
- auto* const thread = system.CurrentScheduler().GetCurrentThread();
auto& kernel = system.Kernel();
- using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type;
Thread::ThreadSynchronizationObjects objects(handle_count);
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
@@ -526,10 +498,13 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
}
thread->CancelWait();
- system.PrepareReschedule(thread->GetProcessorID());
return RESULT_SUCCESS;
}
+static ResultCode CancelSynchronization32(Core::System& system, Handle thread_handle) {
+ return CancelSynchronization(system, thread_handle);
+}
+
/// Attempts to locks a mutex, creating it if it does not already exist
static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle,
VAddr mutex_addr, Handle requesting_thread_handle) {
@@ -538,7 +513,7 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand
"requesting_current_thread_handle=0x{:08X}",
holding_thread_handle, mutex_addr, requesting_thread_handle);
- if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
mutex_addr);
return ERR_INVALID_ADDRESS_STATE;
@@ -554,11 +529,17 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand
requesting_thread_handle);
}
+static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle,
+ u32 mutex_addr, Handle requesting_thread_handle) {
+ return ArbitrateLock(system, holding_thread_handle, static_cast<VAddr>(mutex_addr),
+ requesting_thread_handle);
+}
+
/// Unlock a mutex
static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
- if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
mutex_addr);
return ERR_INVALID_ADDRESS_STATE;
@@ -573,6 +554,10 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
return current_process->GetMutex().Release(mutex_addr);
}
+static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) {
+ return ArbitrateUnlock(system, static_cast<VAddr>(mutex_addr));
+}
+
enum class BreakType : u32 {
Panic = 0,
AssertionFailed = 1,
@@ -673,6 +658,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
if (!break_reason.signal_debugger) {
+ SchedulerLock lock(system.Kernel());
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -683,18 +669,19 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
const auto thread_processor_id = current_thread->GetProcessorID();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
- ASSERT(false);
-
- system.Kernel().CurrentProcess()->PrepareForTermination();
// Kill the current thread
+ system.Kernel().ExceptionalExit();
current_thread->Stop();
- system.PrepareReschedule();
}
}
+static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
+ Break(system, reason, static_cast<u64>(info1), static_cast<u64>(info2));
+}
+
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) {
+static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
if (len == 0) {
return;
}
@@ -707,6 +694,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre
/// Gets system/memory information for the current process
static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle,
u64 info_sub_id) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
@@ -765,6 +753,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: {
if (info_sub_id != 0) {
+ LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
+ info_sub_id);
return ERR_INVALID_ENUM_VALUE;
}
@@ -772,6 +762,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
system.Kernel().CurrentProcess()->GetHandleTable();
const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
if (!process) {
+ LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
+ info_id, info_sub_id, handle);
return ERR_INVALID_HANDLE;
}
@@ -785,35 +777,35 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
return RESULT_SUCCESS;
case GetInfoType::MapRegionBaseAddr:
- *result = process->VMManager().GetMapRegionBaseAddress();
+ *result = process->PageTable().GetAliasRegionStart();
return RESULT_SUCCESS;
case GetInfoType::MapRegionSize:
- *result = process->VMManager().GetMapRegionSize();
+ *result = process->PageTable().GetAliasRegionSize();
return RESULT_SUCCESS;
case GetInfoType::HeapRegionBaseAddr:
- *result = process->VMManager().GetHeapRegionBaseAddress();
+ *result = process->PageTable().GetHeapRegionStart();
return RESULT_SUCCESS;
case GetInfoType::HeapRegionSize:
- *result = process->VMManager().GetHeapRegionSize();
+ *result = process->PageTable().GetHeapRegionSize();
return RESULT_SUCCESS;
case GetInfoType::ASLRRegionBaseAddr:
- *result = process->VMManager().GetASLRRegionBaseAddress();
+ *result = process->PageTable().GetAliasCodeRegionStart();
return RESULT_SUCCESS;
case GetInfoType::ASLRRegionSize:
- *result = process->VMManager().GetASLRRegionSize();
+ *result = process->PageTable().GetAliasCodeRegionSize();
return RESULT_SUCCESS;
case GetInfoType::StackRegionBaseAddr:
- *result = process->VMManager().GetStackRegionBaseAddress();
+ *result = process->PageTable().GetStackRegionStart();
return RESULT_SUCCESS;
case GetInfoType::StackRegionSize:
- *result = process->VMManager().GetStackRegionSize();
+ *result = process->PageTable().GetStackRegionSize();
return RESULT_SUCCESS;
case GetInfoType::TotalPhysicalMemoryAvailable:
@@ -853,7 +845,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
break;
}
- LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
+ LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
return ERR_INVALID_ENUM_VALUE;
}
@@ -863,10 +855,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
case GetInfoType::RegisterResourceLimit: {
if (handle != 0) {
+ LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
return ERR_INVALID_HANDLE;
}
if (info_sub_id != 0) {
+ LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
+ info_sub_id);
return ERR_INVALID_COMBINATION;
}
@@ -936,9 +931,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
- out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks);
+ out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
- out_ticks = core_timing.GetTicks() - prev_ctx_ticks;
+ out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
}
*result = out_ticks;
@@ -946,7 +941,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
}
default:
- LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
+ LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
return ERR_INVALID_ENUM_VALUE;
}
}
@@ -965,6 +960,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
/// Maps memory at a desired address
static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -987,24 +983,38 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
return ERR_INVALID_MEMORY_RANGE;
}
- Process* const current_process = system.Kernel().CurrentProcess();
- auto& vm_manager = current_process->VMManager();
+ Process* const current_process{system.Kernel().CurrentProcess()};
+ auto& page_table{current_process->PageTable()};
if (current_process->GetSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
return ERR_INVALID_STATE;
}
- if (!vm_manager.IsWithinMapRegion(addr, size)) {
- LOG_ERROR(Kernel_SVC, "Range not within map region");
+ if (!page_table.IsInsideAddressSpace(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ if (page_table.IsOutsideAliasRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
return ERR_INVALID_MEMORY_RANGE;
}
- return vm_manager.MapPhysicalMemory(addr, size);
+ return page_table.MapPhysicalMemory(addr, size);
+}
+
+static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
+ return MapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size));
}
/// Unmaps memory previously mapped via MapPhysicalMemory
static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -1027,20 +1037,33 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
return ERR_INVALID_MEMORY_RANGE;
}
- Process* const current_process = system.Kernel().CurrentProcess();
- auto& vm_manager = current_process->VMManager();
+ Process* const current_process{system.Kernel().CurrentProcess()};
+ auto& page_table{current_process->PageTable()};
if (current_process->GetSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
return ERR_INVALID_STATE;
}
- if (!vm_manager.IsWithinMapRegion(addr, size)) {
- LOG_ERROR(Kernel_SVC, "Range not within map region");
+ if (!page_table.IsInsideAddressSpace(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ if (page_table.IsOutsideAliasRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
return ERR_INVALID_MEMORY_RANGE;
}
- return vm_manager.UnmapPhysicalMemory(addr, size);
+ return page_table.UnmapPhysicalMemory(addr, size);
+}
+
+static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
+ return UnmapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size));
}
/// Sets the thread activity
@@ -1072,10 +1095,11 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act
return ERR_BUSY;
}
- thread->SetActivity(static_cast<ThreadActivity>(activity));
+ return thread->SetActivity(static_cast<ThreadActivity>(activity));
+}
- system.PrepareReschedule(thread->GetProcessorID());
- return RESULT_SUCCESS;
+static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) {
+ return SetThreadActivity(system, handle, activity);
}
/// Gets the thread context
@@ -1119,6 +1143,10 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H
return RESULT_SUCCESS;
}
+static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) {
+ return GetThreadContext(system, static_cast<VAddr>(thread_context), handle);
+}
+
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
LOG_TRACE(Kernel_SVC, "called");
@@ -1126,6 +1154,7 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
if (!thread) {
+ *priority = 0;
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
@@ -1160,18 +1189,26 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri
thread->SetPriority(priority);
- system.PrepareReschedule(thread->GetProcessorID());
return RESULT_SUCCESS;
}
+static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) {
+ return SetThreadPriority(system, handle, priority);
+}
+
/// Get which CPU core is executing the current thread
static u32 GetCurrentProcessorNumber(Core::System& system) {
LOG_TRACE(Kernel_SVC, "called");
- return system.CurrentScheduler().GetCurrentThread()->GetProcessorID();
+ return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
+}
+
+static u32 GetCurrentProcessorNumber32(Core::System& system) {
+ return GetCurrentProcessorNumber(system);
}
static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
u64 size, u32 permissions) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC,
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shared_memory_handle, addr, size, permissions);
@@ -1197,79 +1234,61 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
return ERR_INVALID_ADDRESS_STATE;
}
- const auto permissions_type = static_cast<MemoryPermission>(permissions);
- if (permissions_type != MemoryPermission::Read &&
- permissions_type != MemoryPermission::ReadWrite) {
+ const auto permission_type = static_cast<Memory::MemoryPermission>(permissions);
+ if ((permission_type | Memory::MemoryPermission::Write) !=
+ Memory::MemoryPermission::ReadAndWrite) {
LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
permissions);
return ERR_INVALID_MEMORY_PERMISSIONS;
}
- auto* const current_process = system.Kernel().CurrentProcess();
- auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
- if (!shared_memory) {
- LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
- shared_memory_handle);
- return ERR_INVALID_HANDLE;
- }
+ auto* const current_process{system.Kernel().CurrentProcess()};
+ auto& page_table{current_process->PageTable()};
- const auto& vm_manager = current_process->VMManager();
- if (!vm_manager.IsWithinASLRRegion(addr, size)) {
- LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}",
+ if (page_table.IsInvalidRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Addr does not fit within the valid region, addr=0x{:016X}, "
+ "size=0x{:016X}",
addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
- return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
-}
-
-static ResultCode UnmapSharedMemory(Core::System& system, 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 (!Common::Is4KBAligned(addr)) {
- LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
- return ERR_INVALID_ADDRESS;
- }
-
- if (size == 0) {
- LOG_ERROR(Kernel_SVC, "Size is 0");
- return ERR_INVALID_SIZE;
- }
-
- if (!Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
- return ERR_INVALID_SIZE;
+ if (page_table.IsInsideHeapRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Addr does not fit within the heap region, addr=0x{:016X}, "
+ "size=0x{:016X}",
+ addr, size);
+ return ERR_INVALID_MEMORY_RANGE;
}
- if (!IsValidAddressRange(addr, size)) {
- LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ if (page_table.IsInsideAliasRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address does not fit within the map region, addr=0x{:016X}, "
+ "size=0x{:016X}",
addr, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ERR_INVALID_MEMORY_RANGE;
}
- auto* const current_process = system.Kernel().CurrentProcess();
- auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
+ auto shared_memory{current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle)};
if (!shared_memory) {
LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
shared_memory_handle);
return ERR_INVALID_HANDLE;
}
- const auto& vm_manager = current_process->VMManager();
- if (!vm_manager.IsWithinASLRRegion(addr, size)) {
- LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}",
- addr, size);
- return ERR_INVALID_MEMORY_RANGE;
- }
+ return shared_memory->Map(*current_process, addr, size, permission_type);
+}
- return shared_memory->Unmap(*current_process, addr, size);
+static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr,
+ u32 size, u32 permissions) {
+ return MapSharedMemory(system, shared_memory_handle, static_cast<VAddr>(addr),
+ static_cast<std::size_t>(size), permissions);
}
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle);
@@ -1279,18 +1298,17 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
return ERR_INVALID_HANDLE;
}
- auto& memory = system.Memory();
- const auto& vm_manager = process->VMManager();
- const MemoryInfo memory_info = vm_manager.QueryMemory(address);
-
- memory.Write64(memory_info_address, memory_info.base_address);
- memory.Write64(memory_info_address + 8, memory_info.size);
- memory.Write32(memory_info_address + 16, memory_info.state);
- memory.Write32(memory_info_address + 20, memory_info.attributes);
- memory.Write32(memory_info_address + 24, memory_info.permission);
- memory.Write32(memory_info_address + 32, memory_info.ipc_ref_count);
- memory.Write32(memory_info_address + 28, memory_info.device_ref_count);
- memory.Write32(memory_info_address + 36, 0);
+ auto& memory{system.Memory()};
+ const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
+
+ memory.Write64(memory_info_address + 0x00, memory_info.addr);
+ memory.Write64(memory_info_address + 0x08, memory_info.size);
+ memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
+ memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr));
+ memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm));
+ memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount);
+ memory.Write32(memory_info_address + 0x20, memory_info.device_refcount);
+ memory.Write32(memory_info_address + 0x24, 0);
// Page info appears to be currently unused by the kernel and is always set to zero.
memory.Write32(page_info_address, 0);
@@ -1362,8 +1380,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
return ERR_INVALID_HANDLE;
}
- auto& vm_manager = process->VMManager();
- if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
+ auto& page_table = process->PageTable();
+ if (!page_table.IsInsideAddressSpace(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
@@ -1371,7 +1389,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
return ERR_INVALID_ADDRESS_STATE;
}
- if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
+ if (!page_table.IsInsideASLRRegion(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
"size=0x{:016X}).",
@@ -1379,7 +1397,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
return ERR_INVALID_MEMORY_RANGE;
}
- return vm_manager.MapCodeMemory(dst_address, src_address, size);
+ return page_table.MapProcessCodeMemory(dst_address, src_address, size);
}
static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
@@ -1430,8 +1448,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ERR_INVALID_HANDLE;
}
- auto& vm_manager = process->VMManager();
- if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
+ auto& page_table = process->PageTable();
+ if (!page_table.IsInsideAddressSpace(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
@@ -1439,7 +1457,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ERR_INVALID_ADDRESS_STATE;
}
- if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
+ if (!page_table.IsInsideASLRRegion(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
"size=0x{:016X}).",
@@ -1447,12 +1465,13 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ERR_INVALID_MEMORY_RANGE;
}
- return vm_manager.UnmapCodeMemory(dst_address, src_address, size);
+ return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
}
/// Exits the current process
static void ExitProcess(Core::System& system) {
auto* current_process = system.Kernel().CurrentProcess();
+ UNIMPLEMENTED();
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -1462,8 +1481,10 @@ static void ExitProcess(Core::System& system) {
// Kill the current thread
system.CurrentScheduler().GetCurrentThread()->Stop();
+}
- system.PrepareReschedule();
+static void ExitProcess32(Core::System& system) {
+ ExitProcess(system);
}
/// Creates a new thread
@@ -1506,9 +1527,13 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
}
auto& kernel = system.Kernel();
+
+ ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1));
+
+ ThreadType type = THREADTYPE_USER;
CASCADE_RESULT(std::shared_ptr<Thread> thread,
- Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top,
- *current_process));
+ Thread::Create(system, type, "", entry_point, priority, arg, processor_id,
+ stack_top, current_process));
const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
if (new_thread_handle.Failed()) {
@@ -1522,11 +1547,15 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
thread->SetName(
fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
- system.PrepareReschedule(thread->GetProcessorID());
-
return RESULT_SUCCESS;
}
+static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
+ u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
+ return CreateThread(system, out_handle, static_cast<VAddr>(entry_point), static_cast<u64>(arg),
+ static_cast<VAddr>(stack_top), priority, processor_id);
+}
+
/// Starts the thread for the provided handle
static ResultCode StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
@@ -1541,13 +1570,11 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
- thread->ResumeFromWait();
-
- if (thread->GetStatus() == ThreadStatus::Ready) {
- system.PrepareReschedule(thread->GetProcessorID());
- }
+ return thread->Start();
+}
- return RESULT_SUCCESS;
+static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
+ return StartThread(system, thread_handle);
}
/// Called when a thread exits
@@ -1555,9 +1582,12 @@ static void ExitThread(Core::System& system) {
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
- current_thread->Stop();
system.GlobalScheduler().RemoveThread(SharedFrom(current_thread));
- system.PrepareReschedule();
+ current_thread->Stop();
+}
+
+static void ExitThread32(Core::System& system) {
+ ExitThread(system);
}
/// Sleep the current thread
@@ -1576,15 +1606,21 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
if (nanoseconds <= 0) {
switch (static_cast<SleepType>(nanoseconds)) {
- case SleepType::YieldWithoutLoadBalancing:
- is_redundant = current_thread->YieldSimple();
+ case SleepType::YieldWithoutLoadBalancing: {
+ auto pair = current_thread->YieldSimple();
+ is_redundant = pair.second;
break;
- case SleepType::YieldWithLoadBalancing:
- is_redundant = current_thread->YieldAndBalanceLoad();
+ }
+ case SleepType::YieldWithLoadBalancing: {
+ auto pair = current_thread->YieldAndBalanceLoad();
+ is_redundant = pair.second;
break;
- case SleepType::YieldAndWaitForLoadBalancing:
- is_redundant = current_thread->YieldAndWaitForLoadBalancing();
+ }
+ case SleepType::YieldAndWaitForLoadBalancing: {
+ auto pair = current_thread->YieldAndWaitForLoadBalancing();
+ is_redundant = pair.second;
break;
+ }
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
@@ -1592,13 +1628,18 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
current_thread->Sleep(nanoseconds);
}
- if (is_redundant) {
- // If it's redundant, the core is pretty much idle. Some games keep idling
- // a core while it's doing nothing, we advance timing to avoid costly continuous
- // calls.
- system.CoreTiming().AddTicks(2000);
+ if (is_redundant && !system.Kernel().IsMulticore()) {
+ system.Kernel().ExitSVCProfile();
+ system.CoreTiming().AddTicks(1000U);
+ system.GetCpuManager().PreemptSingleCore();
+ system.Kernel().EnterSVCProfile();
}
- system.PrepareReschedule(current_thread->GetProcessorID());
+}
+
+static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
+ const s64 nanoseconds = static_cast<s64>(static_cast<u64>(nanoseconds_low) |
+ (static_cast<u64>(nanoseconds_high) << 32));
+ SleepThread(system, nanoseconds);
}
/// Wait process wide key atomic
@@ -1610,7 +1651,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
- if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
LOG_ERROR(
Kernel_SVC,
"Given mutex address must not be within the kernel address space. address=0x{:016X}",
@@ -1625,31 +1666,69 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
}
ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
-
+ auto& kernel = system.Kernel();
+ Handle event_handle;
+ Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
auto* const current_process = system.Kernel().CurrentProcess();
- const auto& handle_table = current_process->GetHandleTable();
- std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
- ASSERT(thread);
+ {
+ SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds);
+ const auto& handle_table = current_process->GetHandleTable();
+ std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ ASSERT(thread);
+
+ current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
+
+ if (thread->IsPendingTermination()) {
+ lock.CancelSleep();
+ return ERR_THREAD_TERMINATING;
+ }
+
+ const auto release_result = current_process->GetMutex().Release(mutex_addr);
+ if (release_result.IsError()) {
+ lock.CancelSleep();
+ return release_result;
+ }
- const auto release_result = current_process->GetMutex().Release(mutex_addr);
- if (release_result.IsError()) {
- return release_result;
+ if (nano_seconds == 0) {
+ lock.CancelSleep();
+ return RESULT_TIMEOUT;
+ }
+
+ current_thread->SetCondVarWaitAddress(condition_variable_addr);
+ current_thread->SetMutexWaitAddress(mutex_addr);
+ current_thread->SetWaitHandle(thread_handle);
+ current_thread->SetStatus(ThreadStatus::WaitCondVar);
+ current_process->InsertConditionVariableThread(SharedFrom(current_thread));
}
- Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
- current_thread->SetCondVarWaitAddress(condition_variable_addr);
- current_thread->SetMutexWaitAddress(mutex_addr);
- current_thread->SetWaitHandle(thread_handle);
- current_thread->SetStatus(ThreadStatus::WaitCondVar);
- current_thread->InvalidateWakeupCallback();
- current_process->InsertConditionVariableThread(SharedFrom(current_thread));
+ if (event_handle != InvalidHandle) {
+ auto& time_manager = kernel.TimeManager();
+ time_manager.UnscheduleTimeEvent(event_handle);
+ }
+
+ {
+ SchedulerLock lock(kernel);
- current_thread->WakeAfterDelay(nano_seconds);
+ auto* owner = current_thread->GetLockOwner();
+ if (owner != nullptr) {
+ owner->RemoveMutexWaiter(SharedFrom(current_thread));
+ }
+ current_process->RemoveConditionVariableThread(SharedFrom(current_thread));
+ }
// Note: Deliberately don't attempt to inherit the lock owner's priority.
- system.PrepareReschedule(current_thread->GetProcessorID());
- return RESULT_SUCCESS;
+ return current_thread->GetSignalingResult();
+}
+
+static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr,
+ u32 condition_variable_addr, Handle thread_handle,
+ u32 nanoseconds_low, u32 nanoseconds_high) {
+ const s64 nanoseconds =
+ static_cast<s64>(nanoseconds_low | (static_cast<u64>(nanoseconds_high) << 32));
+ return WaitProcessWideKeyAtomic(system, static_cast<VAddr>(mutex_addr),
+ static_cast<VAddr>(condition_variable_addr), thread_handle,
+ nanoseconds);
}
/// Signal process wide key
@@ -1660,16 +1739,18 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
// Retrieve a list of all threads that are waiting for this condition variable.
- auto* const current_process = system.Kernel().CurrentProcess();
+ auto& kernel = system.Kernel();
+ SchedulerLock lock(kernel);
+ auto* const current_process = kernel.CurrentProcess();
std::vector<std::shared_ptr<Thread>> waiting_threads =
current_process->GetConditionVariableThreads(condition_variable_addr);
// Only process up to 'target' threads, unless 'target' is less equal 0, in which case process
// them all.
std::size_t last = waiting_threads.size();
- if (target > 0)
+ if (target > 0) {
last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
-
+ }
for (std::size_t index = 0; index < last; ++index) {
auto& thread = waiting_threads[index];
@@ -1677,21 +1758,17 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
// liberate Cond Var Thread.
current_process->RemoveConditionVariableThread(thread);
- thread->SetCondVarWaitAddress(0);
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
- auto& memory = system.Memory();
// Atomically read the value of the mutex.
u32 mutex_val = 0;
u32 update_val = 0;
const VAddr mutex_address = thread->GetMutexWaitAddress();
do {
- monitor.SetExclusive(current_core, mutex_address);
-
// If the mutex is not yet acquired, acquire it.
- mutex_val = memory.Read32(mutex_address);
+ mutex_val = monitor.ExclusiveRead32(current_core, mutex_address);
if (mutex_val != 0) {
update_val = mutex_val | Mutex::MutexHasWaitersFlag;
@@ -1699,33 +1776,28 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
update_val = thread->GetWaitHandle();
}
} while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val));
+ monitor.ClearExclusive();
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
- ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
- thread->ResumeFromWait();
-
auto* const lock_owner = thread->GetLockOwner();
if (lock_owner != nullptr) {
lock_owner->RemoveMutexWaiter(thread);
}
thread->SetLockOwner(nullptr);
- thread->SetMutexWaitAddress(0);
- thread->SetWaitHandle(0);
- thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
- system.PrepareReschedule(thread->GetProcessorID());
+ thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
+ thread->ResumeFromWait();
} else {
// The mutex is already owned by some other thread, make this thread wait on it.
const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
- ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
- thread->InvalidateWakeupCallback();
- thread->SetStatus(ThreadStatus::WaitMutex);
+ if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
+ thread->SetStatus(ThreadStatus::WaitMutex);
+ }
owner->AddMutexWaiter(thread);
- system.PrepareReschedule(thread->GetProcessorID());
}
}
}
@@ -1741,7 +1813,7 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
type, value, timeout);
// If the passed address is a kernel virtual address, return invalid memory state.
- if (Memory::IsKernelVirtualAddress(address)) {
+ if (Core::Memory::IsKernelVirtualAddress(address)) {
LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
@@ -1756,12 +1828,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
const ResultCode result =
address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
- if (result == RESULT_SUCCESS) {
- system.PrepareReschedule();
- }
return result;
}
+static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value,
+ u32 timeout_low, u32 timeout_high) {
+ s64 timeout = static_cast<s64>(timeout_low | (static_cast<u64>(timeout_high) << 32));
+ return WaitForAddress(system, static_cast<VAddr>(address), type, value, timeout);
+}
+
// Signals to an address (via Address Arbiter)
static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
s32 num_to_wake) {
@@ -1769,7 +1844,7 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type,
address, type, value, num_to_wake);
// If the passed address is a kernel virtual address, return invalid memory state.
- if (Memory::IsKernelVirtualAddress(address)) {
+ if (Core::Memory::IsKernelVirtualAddress(address)) {
LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
@@ -1785,6 +1860,11 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type,
return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
}
+static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value,
+ s32 num_to_wake) {
+ return SignalToAddress(system, static_cast<VAddr>(address), type, value, num_to_wake);
+}
+
static void KernelDebug([[maybe_unused]] Core::System& system,
[[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1,
[[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) {
@@ -1803,14 +1883,21 @@ static u64 GetSystemTick(Core::System& system) {
auto& core_timing = system.CoreTiming();
// Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
- const u64 result{Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks())};
+ const u64 result{system.CoreTiming().GetClockTicks()};
- // Advance time to defeat dumb games that busy-wait for the frame to end.
- core_timing.AddTicks(400);
+ if (!system.Kernel().IsMulticore()) {
+ core_timing.AddTicks(400U);
+ }
return result;
}
+static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
+ u64 time = GetSystemTick(system);
+ *time_low = static_cast<u32>(time);
+ *time_high = static_cast<u32>(time >> 32);
+}
+
/// Close a handle
static ResultCode CloseHandle(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
@@ -1843,9 +1930,14 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) {
return ERR_INVALID_HANDLE;
}
+static ResultCode ResetSignal32(Core::System& system, Handle handle) {
+ return ResetSignal(system, handle);
+}
+
/// Creates a TransferMemory object
static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size,
u32 permissions) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
permissions);
@@ -1865,9 +1957,9 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
return ERR_INVALID_ADDRESS_STATE;
}
- const auto perms = static_cast<MemoryPermission>(permissions);
- if (perms != MemoryPermission::None && perms != MemoryPermission::Read &&
- perms != MemoryPermission::ReadWrite) {
+ const auto perms{static_cast<Memory::MemoryPermission>(permissions)};
+ if (perms > Memory::MemoryPermission::ReadAndWrite ||
+ perms == Memory::MemoryPermission::Write) {
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
permissions);
return ERR_INVALID_MEMORY_PERMISSIONS;
@@ -1890,109 +1982,10 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
return RESULT_SUCCESS;
}
-static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size,
- u32 permission_raw) {
- LOG_DEBUG(Kernel_SVC,
- "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}",
- handle, address, size, permission_raw);
-
- if (!Common::Is4KBAligned(address)) {
- LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
- address);
- return ERR_INVALID_ADDRESS;
- }
-
- if (size == 0 || !Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC,
- "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
- size);
- return ERR_INVALID_SIZE;
- }
-
- if (!IsValidAddressRange(address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Given address and size overflows the 64-bit range (address=0x{:016X}, "
- "size=0x{:016X}).",
- address, size);
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- const auto permissions = static_cast<MemoryPermission>(permission_raw);
- if (permissions != MemoryPermission::None && permissions != MemoryPermission::Read &&
- permissions != MemoryPermission::ReadWrite) {
- LOG_ERROR(Kernel_SVC, "Invalid transfer memory permissions given (permissions=0x{:08X}).",
- permission_raw);
- return ERR_INVALID_STATE;
- }
-
- const auto& kernel = system.Kernel();
- const auto* const current_process = kernel.CurrentProcess();
- const auto& handle_table = current_process->GetHandleTable();
-
- auto transfer_memory = handle_table.Get<TransferMemory>(handle);
- if (!transfer_memory) {
- LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
- handle);
- return ERR_INVALID_HANDLE;
- }
-
- if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Given address and size don't fully fit within the ASLR region "
- "(address=0x{:016X}, size=0x{:016X}).",
- address, size);
- return ERR_INVALID_MEMORY_RANGE;
- }
-
- return transfer_memory->MapMemory(address, size, permissions);
-}
-
-static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address,
- u64 size) {
- LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle,
- address, size);
-
- if (!Common::Is4KBAligned(address)) {
- LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
- address);
- return ERR_INVALID_ADDRESS;
- }
-
- if (size == 0 || !Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC,
- "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
- size);
- return ERR_INVALID_SIZE;
- }
-
- if (!IsValidAddressRange(address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Given address and size overflows the 64-bit range (address=0x{:016X}, "
- "size=0x{:016X}).",
- address, size);
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- const auto& kernel = system.Kernel();
- const auto* const current_process = kernel.CurrentProcess();
- const auto& handle_table = current_process->GetHandleTable();
-
- auto transfer_memory = handle_table.Get<TransferMemory>(handle);
- if (!transfer_memory) {
- LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
- handle);
- return ERR_INVALID_HANDLE;
- }
-
- if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Given address and size don't fully fit within the ASLR region "
- "(address=0x{:016X}, size=0x{:016X}).",
- address, size);
- return ERR_INVALID_MEMORY_RANGE;
- }
-
- return transfer_memory->UnmapMemory(address, size);
+static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u32 addr, u32 size,
+ u32 permissions) {
+ return CreateTransferMemory(system, handle, static_cast<VAddr>(addr),
+ static_cast<std::size_t>(size), permissions);
}
static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
@@ -2004,6 +1997,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
+ *core = 0;
+ *mask = 0;
return ERR_INVALID_HANDLE;
}
@@ -2013,6 +2008,15 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
return RESULT_SUCCESS;
}
+static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core,
+ u32* mask_low, u32* mask_high) {
+ u64 mask{};
+ const auto result = GetThreadCoreMask(system, thread_handle, core, &mask);
+ *mask_high = static_cast<u32>(mask >> 32);
+ *mask_low = static_cast<u32>(mask);
+ return result;
+}
+
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
u64 affinity_mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
@@ -2044,7 +2048,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
return ERR_INVALID_COMBINATION;
}
- if (core < Core::NUM_CPU_CORES) {
+ if (core < Core::Hardware::NUM_CPU_CORES) {
if ((affinity_mask & (1ULL << core)) == 0) {
LOG_ERROR(Kernel_SVC,
"Core is not enabled for the current mask, core={}, mask={:016X}", core,
@@ -2066,57 +2070,14 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
return ERR_INVALID_HANDLE;
}
- system.PrepareReschedule(thread->GetProcessorID());
- thread->ChangeCore(core, affinity_mask);
- system.PrepareReschedule(thread->GetProcessorID());
-
- return RESULT_SUCCESS;
+ return thread->SetCoreAndAffinityMask(core, affinity_mask);
}
-static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size,
- u32 local_permissions, u32 remote_permissions) {
- LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
- local_permissions, remote_permissions);
- if (size == 0) {
- LOG_ERROR(Kernel_SVC, "Size is 0");
- return ERR_INVALID_SIZE;
- }
- if (!Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
- return ERR_INVALID_SIZE;
- }
-
- if (size >= MAIN_MEMORY_SIZE) {
- LOG_ERROR(Kernel_SVC, "Size is not less than 8GB, 0x{:016X}", size);
- return ERR_INVALID_SIZE;
- }
-
- const auto local_perms = static_cast<MemoryPermission>(local_permissions);
- if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) {
- LOG_ERROR(Kernel_SVC,
- "Invalid local memory permissions, expected Read or ReadWrite but got "
- "local_permissions={}",
- static_cast<u32>(local_permissions));
- return ERR_INVALID_MEMORY_PERMISSIONS;
- }
-
- const auto remote_perms = static_cast<MemoryPermission>(remote_permissions);
- if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite &&
- remote_perms != MemoryPermission::DontCare) {
- LOG_ERROR(Kernel_SVC,
- "Invalid remote memory permissions, expected Read, ReadWrite or DontCare but got "
- "remote_permissions={}",
- static_cast<u32>(remote_permissions));
- return ERR_INVALID_MEMORY_PERMISSIONS;
- }
-
- auto& kernel = system.Kernel();
- auto process = kernel.CurrentProcess();
- auto& handle_table = process->GetHandleTable();
- auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms);
-
- CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
- return RESULT_SUCCESS;
+static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core,
+ u32 affinity_mask_low, u32 affinity_mask_high) {
+ const u64 affinity_mask =
+ static_cast<u64>(affinity_mask_low) | (static_cast<u64>(affinity_mask_high) << 32);
+ return SetThreadCoreMask(system, thread_handle, core, affinity_mask);
}
static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
@@ -2147,6 +2108,10 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle
return RESULT_SUCCESS;
}
+static ResultCode CreateEvent32(Core::System& system, Handle* write_handle, Handle* read_handle) {
+ return CreateEvent(system, write_handle, read_handle);
+}
+
static ResultCode ClearEvent(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
@@ -2168,6 +2133,10 @@ static ResultCode ClearEvent(Core::System& system, Handle handle) {
return ERR_INVALID_HANDLE;
}
+static ResultCode ClearEvent32(Core::System& system, Handle handle) {
+ return ClearEvent(system, handle);
+}
+
static ResultCode SignalEvent(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
@@ -2180,10 +2149,13 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) {
}
writable_event->Signal();
- system.PrepareReschedule();
return RESULT_SUCCESS;
}
+static ResultCode SignalEvent32(Core::System& system, Handle handle) {
+ return SignalEvent(system, handle);
+}
+
static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
@@ -2211,6 +2183,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
}
static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
+ std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC, "called");
auto& kernel = system.Kernel();
@@ -2305,11 +2278,10 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
}
const auto& kernel = system.Kernel();
- const auto& vm_manager = kernel.CurrentProcess()->VMManager();
const auto total_copy_size = out_process_ids_size * sizeof(u64);
- if (out_process_ids_size > 0 &&
- !vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) {
+ if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
+ out_process_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
out_process_ids, out_process_ids + total_copy_size);
return ERR_INVALID_ADDRESS_STATE;
@@ -2345,11 +2317,10 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
}
const auto* const current_process = system.Kernel().CurrentProcess();
- const auto& vm_manager = current_process->VMManager();
const auto total_copy_size = out_thread_ids_size * sizeof(u64);
if (out_thread_ids_size > 0 &&
- !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) {
+ !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
out_thread_ids, out_thread_ids + total_copy_size);
return ERR_INVALID_ADDRESS_STATE;
@@ -2370,6 +2341,15 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
return RESULT_SUCCESS;
}
+static ResultCode FlushProcessDataCache32(Core::System& system, Handle handle, u32 address,
+ u32 size) {
+ // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a nope
+ // as all emulation is done in the same cache level in host architecture, thus data cache
+ // does not need flushing.
+ LOG_DEBUG(Kernel_SVC, "called");
+ return RESULT_SUCCESS;
+}
+
namespace {
struct FunctionDef {
using Func = void(Core::System&);
@@ -2384,57 +2364,57 @@ static const FunctionDef SVC_Table_32[] = {
{0x00, nullptr, "Unknown"},
{0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
{0x02, nullptr, "Unknown"},
- {0x03, nullptr, "SetMemoryAttribute32"},
- {0x04, nullptr, "MapMemory32"},
- {0x05, nullptr, "UnmapMemory32"},
+ {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
+ {0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
+ {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
{0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"},
- {0x07, nullptr, "ExitProcess32"},
- {0x08, nullptr, "CreateThread32"},
- {0x09, nullptr, "StartThread32"},
- {0x0a, nullptr, "ExitThread32"},
- {0x0b, nullptr, "SleepThread32"},
+ {0x07, SvcWrap32<ExitProcess32>, "ExitProcess32"},
+ {0x08, SvcWrap32<CreateThread32>, "CreateThread32"},
+ {0x09, SvcWrap32<StartThread32>, "StartThread32"},
+ {0x0a, SvcWrap32<ExitThread32>, "ExitThread32"},
+ {0x0b, SvcWrap32<SleepThread32>, "SleepThread32"},
{0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"},
- {0x0d, nullptr, "SetThreadPriority32"},
- {0x0e, nullptr, "GetThreadCoreMask32"},
- {0x0f, nullptr, "SetThreadCoreMask32"},
- {0x10, nullptr, "GetCurrentProcessorNumber32"},
- {0x11, nullptr, "SignalEvent32"},
- {0x12, nullptr, "ClearEvent32"},
- {0x13, nullptr, "MapSharedMemory32"},
+ {0x0d, SvcWrap32<SetThreadPriority32>, "SetThreadPriority32"},
+ {0x0e, SvcWrap32<GetThreadCoreMask32>, "GetThreadCoreMask32"},
+ {0x0f, SvcWrap32<SetThreadCoreMask32>, "SetThreadCoreMask32"},
+ {0x10, SvcWrap32<GetCurrentProcessorNumber32>, "GetCurrentProcessorNumber32"},
+ {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"},
+ {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"},
+ {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"},
{0x14, nullptr, "UnmapSharedMemory32"},
- {0x15, nullptr, "CreateTransferMemory32"},
+ {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"},
{0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
- {0x17, nullptr, "ResetSignal32"},
+ {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"},
{0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"},
- {0x19, nullptr, "CancelSynchronization32"},
- {0x1a, nullptr, "ArbitrateLock32"},
- {0x1b, nullptr, "ArbitrateUnlock32"},
- {0x1c, nullptr, "WaitProcessWideKeyAtomic32"},
+ {0x19, SvcWrap32<CancelSynchronization32>, "CancelSynchronization32"},
+ {0x1a, SvcWrap32<ArbitrateLock32>, "ArbitrateLock32"},
+ {0x1b, SvcWrap32<ArbitrateUnlock32>, "ArbitrateUnlock32"},
+ {0x1c, SvcWrap32<WaitProcessWideKeyAtomic32>, "WaitProcessWideKeyAtomic32"},
{0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
- {0x1e, nullptr, "GetSystemTick32"},
+ {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
{0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
{0x20, nullptr, "Unknown"},
{0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
{0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
{0x23, nullptr, "Unknown"},
- {0x24, nullptr, "GetProcessId32"},
+ {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
{0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
- {0x26, nullptr, "Break32"},
+ {0x26, SvcWrap32<Break32>, "Break32"},
{0x27, nullptr, "OutputDebugString32"},
{0x28, nullptr, "Unknown"},
{0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
{0x2a, nullptr, "Unknown"},
{0x2b, nullptr, "Unknown"},
- {0x2c, nullptr, "MapPhysicalMemory32"},
- {0x2d, nullptr, "UnmapPhysicalMemory32"},
+ {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
+ {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
{0x2e, nullptr, "Unknown"},
{0x2f, nullptr, "Unknown"},
{0x30, nullptr, "Unknown"},
{0x31, nullptr, "Unknown"},
- {0x32, nullptr, "SetThreadActivity32"},
- {0x33, nullptr, "GetThreadContext32"},
- {0x34, nullptr, "WaitForAddress32"},
- {0x35, nullptr, "SignalToAddress32"},
+ {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
+ {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
+ {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
+ {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
{0x36, nullptr, "Unknown"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
@@ -2450,7 +2430,7 @@ static const FunctionDef SVC_Table_32[] = {
{0x42, nullptr, "Unknown"},
{0x43, nullptr, "ReplyAndReceive32"},
{0x44, nullptr, "Unknown"},
- {0x45, nullptr, "CreateEvent32"},
+ {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "Unknown"},
@@ -2476,7 +2456,7 @@ static const FunctionDef SVC_Table_32[] = {
{0x5c, nullptr, "Unknown"},
{0x5d, nullptr, "Unknown"},
{0x5e, nullptr, "Unknown"},
- {0x5F, nullptr, "FlushProcessDataCache32"},
+ {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
{0x60, nullptr, "Unknown"},
{0x61, nullptr, "Unknown"},
{0x62, nullptr, "Unknown"},
@@ -2510,7 +2490,7 @@ static const FunctionDef SVC_Table_32[] = {
static const FunctionDef SVC_Table_64[] = {
{0x00, nullptr, "Unknown"},
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
- {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
+ {0x02, nullptr, "SetMemoryPermission"},
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
{0x04, SvcWrap64<MapMemory>, "MapMemory"},
{0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
@@ -2528,7 +2508,7 @@ static const FunctionDef SVC_Table_64[] = {
{0x11, SvcWrap64<SignalEvent>, "SignalEvent"},
{0x12, SvcWrap64<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"},
- {0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"},
+ {0x14, nullptr, "UnmapSharedMemory"},
{0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},
{0x16, SvcWrap64<CloseHandle>, "CloseHandle"},
{0x17, SvcWrap64<ResetSignal>, "ResetSignal"},
@@ -2588,9 +2568,9 @@ static const FunctionDef SVC_Table_64[] = {
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
- {0x50, SvcWrap64<CreateSharedMemory>, "CreateSharedMemory"},
- {0x51, SvcWrap64<MapTransferMemory>, "MapTransferMemory"},
- {0x52, SvcWrap64<UnmapTransferMemory>, "UnmapTransferMemory"},
+ {0x50, nullptr, "CreateSharedMemory"},
+ {0x51, nullptr, "MapTransferMemory"},
+ {0x52, nullptr, "UnmapTransferMemory"},
{0x53, nullptr, "CreateInterruptEvent"},
{0x54, nullptr, "QueryPhysicalAddress"},
{0x55, nullptr, "QueryIoMapping"},
@@ -2654,13 +2634,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) {
return &SVC_Table_64[func_num];
}
-MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
-
-void CallSVC(Core::System& system, u32 immediate) {
- MICROPROFILE_SCOPE(Kernel_SVC);
-
- // Lock the global kernel mutex when we enter the kernel HLE.
- std::lock_guard lock{HLE::g_hle_lock};
+void Call(Core::System& system, u32 immediate) {
+ system.ExitDynarmicProfile();
+ auto& kernel = system.Kernel();
+ kernel.EnterSVCProfile();
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
: GetSVCInfo32(immediate);
@@ -2673,6 +2650,9 @@ void CallSVC(Core::System& system, u32 immediate) {
} else {
LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate);
}
+
+ kernel.ExitSVCProfile();
+ system.EnterDynarmicProfile();
}
-} // namespace Kernel
+} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index c5539ac1c..46e64277e 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -10,8 +10,8 @@ namespace Core {
class System;
}
-namespace Kernel {
+namespace Kernel::Svc {
-void CallSVC(Core::System& system, u32 immediate);
+void Call(Core::System& system, u32 immediate);
-} // namespace Kernel
+} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
new file mode 100644
index 000000000..986724beb
--- /dev/null
+++ b/src/core/hle/kernel/svc_types.h
@@ -0,0 +1,68 @@
+// Copyright 2020 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"
+
+namespace Kernel::Svc {
+
+enum class MemoryState : u32 {
+ Free = 0x00,
+ Io = 0x01,
+ Static = 0x02,
+ Code = 0x03,
+ CodeData = 0x04,
+ Normal = 0x05,
+ Shared = 0x06,
+ Alias = 0x07,
+ AliasCode = 0x08,
+ AliasCodeData = 0x09,
+ Ipc = 0x0A,
+ Stack = 0x0B,
+ ThreadLocal = 0x0C,
+ Transfered = 0x0D,
+ SharedTransfered = 0x0E,
+ SharedCode = 0x0F,
+ Inaccessible = 0x10,
+ NonSecureIpc = 0x11,
+ NonDeviceIpc = 0x12,
+ Kernel = 0x13,
+ GeneratedCode = 0x14,
+ CodeOut = 0x15,
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
+
+enum class MemoryAttribute : u32 {
+ Locked = (1 << 0),
+ IpcLocked = (1 << 1),
+ DeviceShared = (1 << 2),
+ Uncached = (1 << 3),
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
+
+enum class MemoryPermission : u32 {
+ None = (0 << 0),
+ Read = (1 << 0),
+ Write = (1 << 1),
+ Execute = (1 << 2),
+ ReadWrite = Read | Write,
+ ReadExecute = Read | Execute,
+ DontCare = (1 << 28),
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
+
+struct MemoryInfo {
+ u64 addr{};
+ u64 size{};
+ MemoryState state{};
+ MemoryAttribute attr{};
+ MemoryPermission perm{};
+ u32 ipc_refcount{};
+ u32 device_refcount{};
+ u32 padding{};
+};
+
+} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 7d735e3fa..0b6dd9df0 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -350,13 +350,50 @@ void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
}
-// Used by QueryMemory32
+// Used by QueryMemory32, ArbitrateLock32
template <ResultCode func(Core::System&, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn32(system,
func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
}
+// Used by Break32
+template <void func(Core::System&, u32, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2));
+}
+
+// Used by ExitProcess32, ExitThread32
+template <void func(Core::System&)>
+void SvcWrap32(Core::System& system) {
+ func(system);
+}
+
+// Used by GetCurrentProcessorNumber32
+template <u32 func(Core::System&)>
+void SvcWrap32(Core::System& system) {
+ FuncReturn32(system, func(system));
+}
+
+// Used by SleepThread32
+template <void func(Core::System&, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ func(system, Param32(system, 0), Param32(system, 1));
+}
+
+// Used by CreateThread32
+template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
+void SvcWrap32(Core::System& system) {
+ Handle param_1 = 0;
+
+ const u32 retval = func(system, &param_1, Param32(system, 0), Param32(system, 1),
+ Param32(system, 2), Param32(system, 3), Param32(system, 4))
+ .raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
+}
+
// Used by GetInfo32
template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
@@ -393,18 +430,114 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
+// Used by GetSystemTick32
+template <void func(Core::System&, u32*, u32*)>
+void SvcWrap32(Core::System& system) {
+ u32 param_1 = 0;
+ u32 param_2 = 0;
+
+ func(system, &param_1, &param_2);
+ system.CurrentArmInterface().SetReg(0, param_1);
+ system.CurrentArmInterface().SetReg(1, param_2);
+}
+
+// Used by CreateEvent32
+template <ResultCode func(Core::System&, Handle*, Handle*)>
+void SvcWrap32(Core::System& system) {
+ Handle param_1 = 0;
+ Handle param_2 = 0;
+
+ const u32 retval = func(system, &param_1, &param_2).raw;
+ system.CurrentArmInterface().SetReg(1, param_1);
+ system.CurrentArmInterface().SetReg(2, param_2);
+ FuncReturn(system, retval);
+}
+
+// Used by GetThreadId32
+template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)>
+void SvcWrap32(Core::System& system) {
+ u32 param_1 = 0;
+ u32 param_2 = 0;
+ u32 param_3 = 0;
+
+ const u32 retval = func(system, Param32(system, 2), &param_1, &param_2, &param_3).raw;
+ system.CurrentArmInterface().SetReg(1, param_1);
+ system.CurrentArmInterface().SetReg(2, param_2);
+ system.CurrentArmInterface().SetReg(3, param_3);
+ FuncReturn(system, retval);
+}
+
// Used by SignalProcessWideKey32
template <void func(Core::System&, u32, s32)>
void SvcWrap32(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
}
-// Used by SendSyncRequest32
+// Used by SetThreadPriority32
+template <ResultCode func(Core::System&, Handle, u32)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval =
+ func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw;
+ FuncReturn(system, retval);
+}
+
+// Used by SetThreadCoreMask32
+template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval =
+ func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
+ static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
+ .raw;
+ FuncReturn(system, retval);
+}
+
+// Used by WaitProcessWideKeyAtomic32
+template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval =
+ func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
+ static_cast<Handle>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
+ static_cast<u32>(Param(system, 4)))
+ .raw;
+ FuncReturn(system, retval);
+}
+
+// Used by WaitForAddress32
+template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
+ static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)),
+ static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4)))
+ .raw;
+ FuncReturn(system, retval);
+}
+
+// Used by SignalToAddress32
+template <ResultCode func(Core::System&, u32, u32, s32, s32)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval =
+ func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
+ static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
+ .raw;
+ FuncReturn(system, retval);
+}
+
+// Used by SendSyncRequest32, ArbitrateUnlock32
template <ResultCode func(Core::System&, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
+// Used by CreateTransferMemory32
+template <ResultCode func(Core::System&, Handle*, u32, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ Handle handle = 0;
+ const u32 retval =
+ func(system, &handle, Param32(system, 1), Param32(system, 2), Param32(system, 3)).raw;
+ system.CurrentArmInterface().SetReg(1, handle);
+ FuncReturn(system, retval);
+}
+
// Used by WaitSynchronization32
template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)>
void SvcWrap32(Core::System& system) {
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp
index dc37fad1a..8b875d853 100644
--- a/src/core/hle/kernel/synchronization.cpp
+++ b/src/core/hle/kernel/synchronization.cpp
@@ -10,78 +10,106 @@
#include "core/hle/kernel/synchronization.h"
#include "core/hle/kernel/synchronization_object.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/time_manager.h"
namespace Kernel {
-/// Default thread wakeup callback for WaitSynchronization
-static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
- std::shared_ptr<SynchronizationObject> object,
- std::size_t index) {
- ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
-
- if (reason == ThreadWakeupReason::Timeout) {
- thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
- return true;
- }
-
- ASSERT(reason == ThreadWakeupReason::Signal);
- thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
- thread->SetWaitSynchronizationOutput(static_cast<u32>(index));
- return true;
-}
-
Synchronization::Synchronization(Core::System& system) : system{system} {}
void Synchronization::SignalObject(SynchronizationObject& obj) const {
+ auto& kernel = system.Kernel();
+ SchedulerLock lock(kernel);
if (obj.IsSignaled()) {
- obj.WakeupAllWaitingThreads();
+ for (auto thread : obj.GetWaitingThreads()) {
+ if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {
+ if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) {
+ ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
+ ASSERT(thread->IsWaitingSync());
+ }
+ thread->SetSynchronizationResults(&obj, RESULT_SUCCESS);
+ thread->ResumeFromWait();
+ }
+ }
+ obj.ClearWaitingThreads();
}
}
std::pair<ResultCode, Handle> Synchronization::WaitFor(
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) {
+ auto& kernel = system.Kernel();
auto* const thread = system.CurrentScheduler().GetCurrentThread();
- // Find the first object that is acquirable in the provided list of objects
- const auto itr = std::find_if(sync_objects.begin(), sync_objects.end(),
- [thread](const std::shared_ptr<SynchronizationObject>& object) {
- return object->IsSignaled();
- });
-
- if (itr != sync_objects.end()) {
- // We found a ready object, acquire it and set the result value
- SynchronizationObject* object = itr->get();
- object->Acquire(thread);
- const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
- return {RESULT_SUCCESS, index};
+ Handle event_handle = InvalidHandle;
+ {
+ SchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds);
+ const auto itr =
+ std::find_if(sync_objects.begin(), sync_objects.end(),
+ [thread](const std::shared_ptr<SynchronizationObject>& object) {
+ return object->IsSignaled();
+ });
+
+ if (itr != sync_objects.end()) {
+ // We found a ready object, acquire it and set the result value
+ SynchronizationObject* object = itr->get();
+ object->Acquire(thread);
+ const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
+ lock.CancelSleep();
+ return {RESULT_SUCCESS, index};
+ }
+
+ if (nano_seconds == 0) {
+ lock.CancelSleep();
+ return {RESULT_TIMEOUT, InvalidHandle};
+ }
+
+ if (thread->IsPendingTermination()) {
+ lock.CancelSleep();
+ return {ERR_THREAD_TERMINATING, InvalidHandle};
+ }
+
+ if (thread->IsSyncCancelled()) {
+ thread->SetSyncCancelled(false);
+ lock.CancelSleep();
+ return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle};
+ }
+
+ for (auto& object : sync_objects) {
+ object->AddWaitingThread(SharedFrom(thread));
+ }
+
+ thread->SetSynchronizationObjects(&sync_objects);
+ thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
+ thread->SetStatus(ThreadStatus::WaitSynch);
+ thread->SetWaitingSync(true);
}
+ thread->SetWaitingSync(false);
- // No objects were ready to be acquired, prepare to suspend the thread.
-
- // If a timeout value of 0 was provided, just return the Timeout error code instead of
- // suspending the thread.
- if (nano_seconds == 0) {
- return {RESULT_TIMEOUT, InvalidHandle};
+ if (event_handle != InvalidHandle) {
+ auto& time_manager = kernel.TimeManager();
+ time_manager.UnscheduleTimeEvent(event_handle);
}
- if (thread->IsSyncCancelled()) {
- thread->SetSyncCancelled(false);
- return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle};
+ {
+ SchedulerLock lock(kernel);
+ ResultCode signaling_result = thread->GetSignalingResult();
+ SynchronizationObject* signaling_object = thread->GetSignalingObject();
+ thread->SetSynchronizationObjects(nullptr);
+ auto shared_thread = SharedFrom(thread);
+ for (auto& obj : sync_objects) {
+ obj->RemoveWaitingThread(shared_thread);
+ }
+ if (signaling_object != nullptr) {
+ const auto itr = std::find_if(
+ sync_objects.begin(), sync_objects.end(),
+ [signaling_object](const std::shared_ptr<SynchronizationObject>& object) {
+ return object.get() == signaling_object;
+ });
+ ASSERT(itr != sync_objects.end());
+ signaling_object->Acquire(thread);
+ const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
+ return {signaling_result, index};
+ }
+ return {signaling_result, -1};
}
-
- for (auto& object : sync_objects) {
- object->AddWaitingThread(SharedFrom(thread));
- }
-
- thread->SetSynchronizationObjects(std::move(sync_objects));
- thread->SetStatus(ThreadStatus::WaitSynch);
-
- // Create an event to wake the thread up after the specified nanosecond delay has passed
- thread->WakeAfterDelay(nano_seconds);
- thread->SetWakeupCallback(DefaultThreadWakeupCallback);
-
- system.PrepareReschedule(thread->GetProcessorID());
-
- return {RESULT_TIMEOUT, InvalidHandle};
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp
index 43f3eef18..ba4d39157 100644
--- a/src/core/hle/kernel/synchronization_object.cpp
+++ b/src/core/hle/kernel/synchronization_object.cpp
@@ -38,68 +38,8 @@ void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread)
waiting_threads.erase(itr);
}
-std::shared_ptr<Thread> SynchronizationObject::GetHighestPriorityReadyThread() const {
- Thread* candidate = nullptr;
- u32 candidate_priority = THREADPRIO_LOWEST + 1;
-
- for (const auto& thread : waiting_threads) {
- const ThreadStatus thread_status = thread->GetStatus();
-
- // The list of waiting threads must not contain threads that are not waiting to be awakened.
- ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
- thread_status == ThreadStatus::WaitHLEEvent,
- "Inconsistent thread statuses in waiting_threads");
-
- if (thread->GetPriority() >= candidate_priority)
- continue;
-
- if (ShouldWait(thread.get()))
- continue;
-
- candidate = thread.get();
- candidate_priority = thread->GetPriority();
- }
-
- return SharedFrom(candidate);
-}
-
-void SynchronizationObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) {
- ASSERT(!ShouldWait(thread.get()));
-
- if (!thread) {
- return;
- }
-
- if (thread->IsSleepingOnWait()) {
- for (const auto& object : thread->GetSynchronizationObjects()) {
- ASSERT(!object->ShouldWait(thread.get()));
- object->Acquire(thread.get());
- }
- } else {
- Acquire(thread.get());
- }
-
- const std::size_t index = thread->GetSynchronizationObjectIndex(SharedFrom(this));
-
- thread->ClearSynchronizationObjects();
-
- thread->CancelWakeupTimer();
-
- bool resume = true;
- if (thread->HasWakeupCallback()) {
- resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, SharedFrom(this),
- index);
- }
- if (resume) {
- thread->ResumeFromWait();
- kernel.PrepareReschedule(thread->GetProcessorID());
- }
-}
-
-void SynchronizationObject::WakeupAllWaitingThreads() {
- while (auto thread = GetHighestPriorityReadyThread()) {
- WakeupWaitingThread(thread);
- }
+void SynchronizationObject::ClearWaitingThreads() {
+ waiting_threads.clear();
}
const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const {
diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h
index 741c31faf..f89b24204 100644
--- a/src/core/hle/kernel/synchronization_object.h
+++ b/src/core/hle/kernel/synchronization_object.h
@@ -12,6 +12,7 @@
namespace Kernel {
class KernelCore;
+class Synchronization;
class Thread;
/// Class that represents a Kernel object that a thread can be waiting on
@@ -49,24 +50,11 @@ public:
*/
void RemoveWaitingThread(std::shared_ptr<Thread> thread);
- /**
- * Wake up all threads waiting on this object that can be awoken, in priority order,
- * and set the synchronization result and output of the thread.
- */
- void WakeupAllWaitingThreads();
-
- /**
- * Wakes up a single thread waiting on this object.
- * @param thread Thread that is waiting on this object to wakeup.
- */
- void WakeupWaitingThread(std::shared_ptr<Thread> thread);
-
- /// Obtains the highest priority thread that is ready to run from this object's waiting list.
- std::shared_ptr<Thread> GetHighestPriorityReadyThread() const;
-
/// Get a const reference to the waiting threads list for debug use
const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
+ void ClearWaitingThreads();
+
protected:
bool is_signaled{}; // Tells if this sync object is signalled;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 83e956036..da0cb26b6 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -9,12 +9,12 @@
#include "common/assert.h"
#include "common/common_types.h"
+#include "common/fiber.h"
#include "common/logging/log.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
-#include "core/core_timing.h"
-#include "core/core_timing_util.h"
+#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
@@ -23,9 +23,15 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
+#ifdef ARCHITECTURE_x86_64
+#include "core/arm/dynarmic/arm_dynarmic_32.h"
+#include "core/arm/dynarmic/arm_dynarmic_64.h"
+#endif
+
namespace Kernel {
bool Thread::ShouldWait(const Thread* thread) const {
@@ -44,47 +50,28 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {}
Thread::~Thread() = default;
void Thread::Stop() {
- // Cancel any outstanding wakeup events for this thread
- Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
- global_handle);
- kernel.GlobalHandleTable().Close(global_handle);
- global_handle = 0;
- SetStatus(ThreadStatus::Dead);
- Signal();
-
- // Clean up any dangling references in objects that this thread was waiting for
- for (auto& wait_object : wait_objects) {
- wait_object->RemoveWaitingThread(SharedFrom(this));
- }
- wait_objects.clear();
-
- owner_process->UnregisterThread(this);
-
- // Mark the TLS slot in the thread's page as free.
- owner_process->FreeTLSRegion(tls_address);
-}
+ {
+ SchedulerLock lock(kernel);
+ SetStatus(ThreadStatus::Dead);
+ Signal();
+ kernel.GlobalHandleTable().Close(global_handle);
-void Thread::WakeAfterDelay(s64 nanoseconds) {
- // Don't schedule a wakeup if the thread wants to wait forever
- if (nanoseconds == -1)
- return;
-
- // This function might be called from any thread so we have to be cautious and use the
- // thread-safe version of ScheduleEvent.
- const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
- Core::System::GetInstance().CoreTiming().ScheduleEvent(
- cycles, kernel.ThreadWakeupCallbackEventType(), global_handle);
-}
+ if (owner_process) {
+ owner_process->UnregisterThread(this);
-void Thread::CancelWakeupTimer() {
- Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
- global_handle);
+ // Mark the TLS slot in the thread's page as free.
+ owner_process->FreeTLSRegion(tls_address);
+ }
+ arm_interface.reset();
+ has_exited = true;
+ }
+ global_handle = 0;
}
void Thread::ResumeFromWait() {
- ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
-
+ SchedulerLock lock(kernel);
switch (status) {
+ case ThreadStatus::Paused:
case ThreadStatus::WaitSynch:
case ThreadStatus::WaitHLEEvent:
case ThreadStatus::WaitSleep:
@@ -92,12 +79,13 @@ void Thread::ResumeFromWait() {
case ThreadStatus::WaitMutex:
case ThreadStatus::WaitCondVar:
case ThreadStatus::WaitArb:
+ case ThreadStatus::Dormant:
break;
case ThreadStatus::Ready:
// The thread's wakeup callback must have already been cleared when the thread was first
// awoken.
- ASSERT(wakeup_callback == nullptr);
+ ASSERT(hle_callback == nullptr);
// If the thread is waiting on multiple wait objects, it might be awoken more than once
// before actually resuming. We can ignore subsequent wakeups if the thread status has
// already been set to ThreadStatus::Ready.
@@ -113,24 +101,31 @@ void Thread::ResumeFromWait() {
return;
}
- wakeup_callback = nullptr;
+ SetStatus(ThreadStatus::Ready);
+}
- if (activity == ThreadActivity::Paused) {
- SetStatus(ThreadStatus::Paused);
- return;
- }
+void Thread::OnWakeUp() {
+ SchedulerLock lock(kernel);
+
+ SetStatus(ThreadStatus::Ready);
+}
+ResultCode Thread::Start() {
+ SchedulerLock lock(kernel);
SetStatus(ThreadStatus::Ready);
+ return RESULT_SUCCESS;
}
void Thread::CancelWait() {
- if (GetSchedulingStatus() != ThreadSchedStatus::Paused) {
+ SchedulerLock lock(kernel);
+ if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) {
is_sync_cancelled = true;
return;
}
+ // TODO(Blinkhawk): Implement cancel of server session
is_sync_cancelled = false;
- SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
- ResumeFromWait();
+ SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
+ SetStatus(ThreadStatus::Ready);
}
static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
@@ -148,16 +143,32 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
context.pc = entry_point;
context.sp = stack_top;
// TODO(merry): Perform a hardware test to determine the below value.
- // AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero
- context.fpcr = 0x03C00000;
+ context.fpcr = 0;
+}
+
+std::shared_ptr<Common::Fiber>& Thread::GetHostContext() {
+ return host_context;
+}
+
+ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
+ std::string name, VAddr entry_point, u32 priority,
+ u64 arg, s32 processor_id, VAddr stack_top,
+ Process* owner_process) {
+ std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
+ void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
+ return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
+ owner_process, std::move(init_func), init_func_parameter);
}
-ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name,
- VAddr entry_point, u32 priority, u64 arg,
- s32 processor_id, VAddr stack_top,
- Process& owner_process) {
+ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
+ std::string name, VAddr entry_point, u32 priority,
+ u64 arg, s32 processor_id, VAddr stack_top,
+ Process* owner_process,
+ std::function<void(void*)>&& thread_start_func,
+ void* thread_start_parameter) {
+ auto& kernel = system.Kernel();
// Check if priority is in ranged. Lowest priority -> highest priority id.
- if (priority > THREADPRIO_LOWEST) {
+ if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
return ERR_INVALID_THREAD_PRIORITY;
}
@@ -167,11 +178,12 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
return ERR_INVALID_PROCESSOR_ID;
}
- auto& system = Core::System::GetInstance();
- if (!system.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 RESULT_UNKNOWN;
+ if (owner_process) {
+ if (!system.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 RESULT_UNKNOWN;
+ }
}
std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
@@ -182,51 +194,72 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
thread->stack_top = stack_top;
thread->tpidr_el0 = 0;
thread->nominal_priority = thread->current_priority = priority;
- thread->last_running_ticks = system.CoreTiming().GetTicks();
+ thread->last_running_ticks = 0;
thread->processor_id = processor_id;
thread->ideal_core = processor_id;
thread->affinity_mask = 1ULL << processor_id;
- thread->wait_objects.clear();
+ thread->wait_objects = nullptr;
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
thread->name = std::move(name);
thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
- thread->owner_process = &owner_process;
- auto& scheduler = kernel.GlobalScheduler();
- scheduler.AddThread(thread);
- thread->tls_address = thread->owner_process->CreateTLSRegion();
+ thread->owner_process = owner_process;
+ thread->type = type_flags;
+ if ((type_flags & THREADTYPE_IDLE) == 0) {
+ auto& scheduler = kernel.GlobalScheduler();
+ scheduler.AddThread(thread);
+ }
+ if (owner_process) {
+ thread->tls_address = thread->owner_process->CreateTLSRegion();
+ thread->owner_process->RegisterThread(thread.get());
+ } else {
+ thread->tls_address = 0;
+ }
- thread->owner_process->RegisterThread(thread.get());
+ thread->arm_interface.reset();
+ if ((type_flags & THREADTYPE_HLE) == 0) {
+#ifdef ARCHITECTURE_x86_64
+ if (owner_process && !owner_process->Is64BitProcess()) {
+ thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
+ system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(),
+ processor_id);
+ } else {
+ thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
+ system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(),
+ processor_id);
+ }
+#else
+#error Platform not supported yet.
+#endif
- ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
- static_cast<u32>(entry_point), static_cast<u32>(arg));
- ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
+ ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
+ static_cast<u32>(entry_point), static_cast<u32>(arg));
+ ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
+ }
+ thread->host_context =
+ std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
}
void Thread::SetPriority(u32 priority) {
+ SchedulerLock lock(kernel);
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value.");
nominal_priority = priority;
UpdatePriority();
}
-void Thread::SetWaitSynchronizationResult(ResultCode result) {
- context_32.cpu_registers[0] = result.raw;
- context_64.cpu_registers[0] = result.raw;
-}
-
-void Thread::SetWaitSynchronizationOutput(s32 output) {
- context_32.cpu_registers[1] = output;
- context_64.cpu_registers[1] = output;
+void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) {
+ signaling_object = object;
+ signaling_result = result;
}
s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
- ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
- const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
- return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
+ ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything");
+ const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object);
+ return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1);
}
VAddr Thread::GetCommandBufferAddress() const {
@@ -235,6 +268,14 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + command_header_offset;
}
+Core::ARM_Interface& Thread::ArmInterface() {
+ return *arm_interface;
+}
+
+const Core::ARM_Interface& Thread::ArmInterface() const {
+ return *arm_interface;
+}
+
void Thread::SetStatus(ThreadStatus new_status) {
if (new_status == status) {
return;
@@ -256,10 +297,6 @@ void Thread::SetStatus(ThreadStatus new_status) {
break;
}
- if (status == ThreadStatus::Running) {
- last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
- }
-
status = new_status;
}
@@ -340,75 +377,116 @@ void Thread::UpdatePriority() {
lock_owner->UpdatePriority();
}
-void Thread::ChangeCore(u32 core, u64 mask) {
- SetCoreAndAffinityMask(core, mask);
-}
-
bool Thread::AllSynchronizationObjectsReady() const {
- return std::none_of(wait_objects.begin(), wait_objects.end(),
+ return std::none_of(wait_objects->begin(), wait_objects->end(),
[this](const std::shared_ptr<SynchronizationObject>& object) {
return object->ShouldWait(this);
});
}
-bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
- std::shared_ptr<SynchronizationObject> object,
- std::size_t index) {
- ASSERT(wakeup_callback);
- return wakeup_callback(reason, std::move(thread), std::move(object), index);
+bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) {
+ ASSERT(hle_callback);
+ return hle_callback(std::move(thread));
}
-void Thread::SetActivity(ThreadActivity value) {
- activity = value;
+ResultCode Thread::SetActivity(ThreadActivity value) {
+ SchedulerLock lock(kernel);
+
+ auto sched_status = GetSchedulingStatus();
+
+ if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) {
+ return ERR_INVALID_STATE;
+ }
+
+ if (IsPendingTermination()) {
+ return RESULT_SUCCESS;
+ }
if (value == ThreadActivity::Paused) {
- // Set status if not waiting
- if (status == ThreadStatus::Ready || status == ThreadStatus::Running) {
- SetStatus(ThreadStatus::Paused);
- kernel.PrepareReschedule(processor_id);
+ if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) {
+ return ERR_INVALID_STATE;
+ }
+ AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
+ } else {
+ if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) {
+ return ERR_INVALID_STATE;
}
- } else if (status == ThreadStatus::Paused) {
- // Ready to reschedule
- ResumeFromWait();
+ RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
}
+ return RESULT_SUCCESS;
}
-void Thread::Sleep(s64 nanoseconds) {
- // Sleep current thread and check for next thread to schedule
- SetStatus(ThreadStatus::WaitSleep);
+ResultCode Thread::Sleep(s64 nanoseconds) {
+ Handle event_handle{};
+ {
+ SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds);
+ SetStatus(ThreadStatus::WaitSleep);
+ }
- // Create an event to wake the thread up after the specified nanosecond delay has passed
- WakeAfterDelay(nanoseconds);
+ if (event_handle != InvalidHandle) {
+ auto& time_manager = kernel.TimeManager();
+ time_manager.UnscheduleTimeEvent(event_handle);
+ }
+ return RESULT_SUCCESS;
}
-bool Thread::YieldSimple() {
- auto& scheduler = kernel.GlobalScheduler();
- return scheduler.YieldThread(this);
+std::pair<ResultCode, bool> Thread::YieldSimple() {
+ bool is_redundant = false;
+ {
+ SchedulerLock lock(kernel);
+ is_redundant = kernel.GlobalScheduler().YieldThread(this);
+ }
+ return {RESULT_SUCCESS, is_redundant};
+}
+
+std::pair<ResultCode, bool> Thread::YieldAndBalanceLoad() {
+ bool is_redundant = false;
+ {
+ SchedulerLock lock(kernel);
+ is_redundant = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this);
+ }
+ return {RESULT_SUCCESS, is_redundant};
+}
+
+std::pair<ResultCode, bool> Thread::YieldAndWaitForLoadBalancing() {
+ bool is_redundant = false;
+ {
+ SchedulerLock lock(kernel);
+ is_redundant = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this);
+ }
+ return {RESULT_SUCCESS, is_redundant};
}
-bool Thread::YieldAndBalanceLoad() {
- auto& scheduler = kernel.GlobalScheduler();
- return scheduler.YieldThreadAndBalanceLoad(this);
+void Thread::AddSchedulingFlag(ThreadSchedFlags flag) {
+ const u32 old_state = scheduling_state;
+ pausing_state |= static_cast<u32>(flag);
+ const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus());
+ scheduling_state = base_scheduling | pausing_state;
+ kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
}
-bool Thread::YieldAndWaitForLoadBalancing() {
- auto& scheduler = kernel.GlobalScheduler();
- return scheduler.YieldThreadAndWaitForLoadBalancing(this);
+void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) {
+ const u32 old_state = scheduling_state;
+ pausing_state &= ~static_cast<u32>(flag);
+ const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus());
+ scheduling_state = base_scheduling | pausing_state;
+ kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
}
void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
- const u32 old_flags = scheduling_state;
+ const u32 old_state = scheduling_state;
scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) |
static_cast<u32>(new_status);
- AdjustSchedulingOnStatus(old_flags);
+ kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
}
void Thread::SetCurrentPriority(u32 new_priority) {
const u32 old_priority = std::exchange(current_priority, new_priority);
- AdjustSchedulingOnPriority(old_priority);
+ kernel.GlobalScheduler().AdjustSchedulingOnPriority(this, old_priority);
}
ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
+ SchedulerLock lock(kernel);
const auto HighestSetCore = [](u64 mask, u32 max_cores) {
for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) {
if (((mask >> core) & 1) != 0) {
@@ -422,6 +500,8 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
if (new_core == THREADPROCESSORID_DONT_UPDATE) {
new_core = use_override ? ideal_core_override : ideal_core;
if ((new_affinity_mask & (1ULL << new_core)) == 0) {
+ LOG_ERROR(Kernel, "New affinity mask is incorrect! new_core={}, new_affinity_mask={}",
+ new_core, new_affinity_mask);
return ERR_INVALID_COMBINATION;
}
}
@@ -440,118 +520,10 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
processor_id = ideal_core;
}
}
- AdjustSchedulingOnAffinity(old_affinity_mask, old_core);
+ kernel.GlobalScheduler().AdjustSchedulingOnAffinity(this, old_affinity_mask, old_core);
}
}
return RESULT_SUCCESS;
}
-void Thread::AdjustSchedulingOnStatus(u32 old_flags) {
- if (old_flags == scheduling_state) {
- return;
- }
-
- auto& scheduler = kernel.GlobalScheduler();
- if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) ==
- ThreadSchedStatus::Runnable) {
- // In this case the thread was running, now it's pausing/exitting
- if (processor_id >= 0) {
- scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this);
- }
-
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
- scheduler.Unsuggest(current_priority, core, this);
- }
- }
- } else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
- // The thread is now set to running from being stopped
- if (processor_id >= 0) {
- scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this);
- }
-
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
- scheduler.Suggest(current_priority, core, this);
- }
- }
- }
-
- scheduler.SetReselectionPending();
-}
-
-void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
- if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
- return;
- }
- auto& scheduler = kernel.GlobalScheduler();
- if (processor_id >= 0) {
- scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this);
- }
-
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
- scheduler.Unsuggest(old_priority, core, this);
- }
- }
-
- // Add thread to the new priority queues.
- Thread* current_thread = GetCurrentThread();
-
- if (processor_id >= 0) {
- if (current_thread == this) {
- scheduler.SchedulePrepend(current_priority, static_cast<u32>(processor_id), this);
- } else {
- scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this);
- }
- }
-
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
- scheduler.Suggest(current_priority, core, this);
- }
- }
-
- scheduler.SetReselectionPending();
-}
-
-void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
- auto& scheduler = kernel.GlobalScheduler();
- if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
- current_priority >= THREADPRIO_COUNT) {
- return;
- }
-
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (((old_affinity_mask >> core) & 1) != 0) {
- if (core == static_cast<u32>(old_core)) {
- scheduler.Unschedule(current_priority, core, this);
- } else {
- scheduler.Unsuggest(current_priority, core, this);
- }
- }
- }
-
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (((affinity_mask >> core) & 1) != 0) {
- if (core == static_cast<u32>(processor_id)) {
- scheduler.Schedule(current_priority, core, this);
- } else {
- scheduler.Suggest(current_priority, core, this);
- }
- }
- }
-
- scheduler.SetReselectionPending();
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Gets the current thread
- */
-Thread* GetCurrentThread() {
- return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
-}
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 23fdef8a4..8daf79fac 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -6,26 +6,47 @@
#include <functional>
#include <string>
+#include <utility>
#include <vector>
#include "common/common_types.h"
+#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/synchronization_object.h"
#include "core/hle/result.h"
+namespace Common {
+class Fiber;
+}
+
+namespace Core {
+class ARM_Interface;
+class System;
+} // namespace Core
+
namespace Kernel {
+class GlobalScheduler;
class KernelCore;
class Process;
class Scheduler;
enum ThreadPriority : u32 {
- THREADPRIO_HIGHEST = 0, ///< Highest thread priority
- THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
- THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
- THREADPRIO_LOWEST = 63, ///< Lowest thread priority
- THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
+ THREADPRIO_HIGHEST = 0, ///< Highest thread priority
+ THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration
+ THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
+ THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
+ THREADPRIO_LOWEST = 63, ///< Lowest thread priority
+ THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
+};
+
+enum ThreadType : u32 {
+ THREADTYPE_USER = 0x1,
+ THREADTYPE_KERNEL = 0x2,
+ THREADTYPE_HLE = 0x4,
+ THREADTYPE_IDLE = 0x8,
+ THREADTYPE_SUSPEND = 0x10,
};
enum ThreadProcessorId : s32 {
@@ -107,26 +128,45 @@ public:
using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>;
- using WakeupCallback =
- std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
- std::shared_ptr<SynchronizationObject> object, std::size_t index)>;
+ using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>;
+
+ /**
+ * Creates and returns a new thread. The new thread is immediately scheduled
+ * @param system The instance of the whole system
+ * @param name The friendly name desired for the thread
+ * @param entry_point The address at which the thread should start execution
+ * @param priority The thread's priority
+ * @param arg User data to pass to the thread
+ * @param processor_id The ID(s) of the processors on which the thread is desired to be run
+ * @param stack_top The address of the thread's stack top
+ * @param owner_process The parent process for the thread, if null, it's a kernel thread
+ * @return A shared pointer to the newly created thread
+ */
+ static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
+ std::string name, VAddr entry_point,
+ u32 priority, u64 arg, s32 processor_id,
+ VAddr stack_top, Process* owner_process);
/**
* Creates and returns a new thread. The new thread is immediately scheduled
- * @param kernel The kernel instance this thread will be created under.
+ * @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
- * @param owner_process The parent process for the thread
+ * @param owner_process The parent process for the thread, if null, it's a kernel thread
+ * @param thread_start_func The function where the host context will start.
+ * @param thread_start_parameter The parameter which will passed to host context on init
* @return A shared pointer to the newly created thread
*/
- static ResultVal<std::shared_ptr<Thread>> Create(KernelCore& kernel, std::string name,
- VAddr entry_point, u32 priority, u64 arg,
- s32 processor_id, VAddr stack_top,
- Process& owner_process);
+ static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
+ std::string name, VAddr entry_point,
+ u32 priority, u64 arg, s32 processor_id,
+ VAddr stack_top, Process* owner_process,
+ std::function<void(void*)>&& thread_start_func,
+ void* thread_start_parameter);
std::string GetName() const override {
return name;
@@ -181,7 +221,7 @@ public:
void UpdatePriority();
/// Changes the core that the thread is running or scheduled to run on.
- void ChangeCore(u32 core, u64 mask);
+ ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
/**
* Gets the thread's thread ID
@@ -194,6 +234,10 @@ public:
/// Resumes a thread from waiting
void ResumeFromWait();
+ void OnWakeUp();
+
+ ResultCode Start();
+
/// Cancels a waiting operation that this thread may or may not be within.
///
/// When the thread is within a waiting state, this will set the thread's
@@ -202,26 +246,19 @@ public:
///
void CancelWait();
- /**
- * Schedules an event to wake up the specified thread after the specified delay
- * @param nanoseconds The time this thread will be allowed to sleep for
- */
- void WakeAfterDelay(s64 nanoseconds);
+ void SetSynchronizationResults(SynchronizationObject* object, ResultCode result);
- /// Cancel any outstanding wakeup events for this thread
- void CancelWakeupTimer();
+ Core::ARM_Interface& ArmInterface();
- /**
- * Sets the result after the thread awakens (from svcWaitSynchronization)
- * @param result Value to set to the returned result
- */
- void SetWaitSynchronizationResult(ResultCode result);
+ const Core::ARM_Interface& ArmInterface() const;
- /**
- * Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
- * @param output Value to set to the output parameter
- */
- void SetWaitSynchronizationOutput(s32 output);
+ SynchronizationObject* GetSignalingObject() const {
+ return signaling_object;
+ }
+
+ ResultCode GetSignalingResult() const {
+ return signaling_result;
+ }
/**
* Retrieves the index that this particular object occupies in the list of objects
@@ -269,11 +306,6 @@ public:
*/
VAddr GetCommandBufferAddress() const;
- /// Returns whether this thread is waiting on objects from a WaitSynchronization call.
- bool IsSleepingOnWait() const {
- return status == ThreadStatus::WaitSynch;
- }
-
ThreadContext32& GetContext32() {
return context_32;
}
@@ -290,6 +322,28 @@ public:
return context_64;
}
+ bool IsHLEThread() const {
+ return (type & THREADTYPE_HLE) != 0;
+ }
+
+ bool IsSuspendThread() const {
+ return (type & THREADTYPE_SUSPEND) != 0;
+ }
+
+ bool IsIdleThread() const {
+ return (type & THREADTYPE_IDLE) != 0;
+ }
+
+ bool WasRunning() const {
+ return was_running;
+ }
+
+ void SetWasRunning(bool value) {
+ was_running = value;
+ }
+
+ std::shared_ptr<Common::Fiber>& GetHostContext();
+
ThreadStatus GetStatus() const {
return status;
}
@@ -325,18 +379,18 @@ public:
}
const ThreadSynchronizationObjects& GetSynchronizationObjects() const {
- return wait_objects;
+ return *wait_objects;
}
- void SetSynchronizationObjects(ThreadSynchronizationObjects objects) {
- wait_objects = std::move(objects);
+ void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) {
+ wait_objects = objects;
}
void ClearSynchronizationObjects() {
- for (const auto& waiting_object : wait_objects) {
+ for (const auto& waiting_object : *wait_objects) {
waiting_object->RemoveWaitingThread(SharedFrom(this));
}
- wait_objects.clear();
+ wait_objects->clear();
}
/// Determines whether all the objects this thread is waiting on are ready.
@@ -386,26 +440,35 @@ public:
arb_wait_address = address;
}
- bool HasWakeupCallback() const {
- return wakeup_callback != nullptr;
+ bool HasHLECallback() const {
+ return hle_callback != nullptr;
}
- void SetWakeupCallback(WakeupCallback callback) {
- wakeup_callback = std::move(callback);
+ void SetHLECallback(HLECallback callback) {
+ hle_callback = std::move(callback);
}
- void InvalidateWakeupCallback() {
- SetWakeupCallback(nullptr);
+ void SetHLETimeEvent(Handle time_event) {
+ hle_time_event = time_event;
}
- /**
- * Invokes the thread's wakeup callback.
- *
- * @pre A valid wakeup callback has been set. Violating this precondition
- * will cause an assertion to trigger.
- */
- bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
- std::shared_ptr<SynchronizationObject> object, std::size_t index);
+ void SetHLESyncObject(SynchronizationObject* object) {
+ hle_object = object;
+ }
+
+ Handle GetHLETimeEvent() const {
+ return hle_time_event;
+ }
+
+ SynchronizationObject* GetHLESyncObject() const {
+ return hle_object;
+ }
+
+ void InvalidateHLECallback() {
+ SetHLECallback(nullptr);
+ }
+
+ bool InvokeHLECallback(std::shared_ptr<Thread> thread);
u32 GetIdealCore() const {
return ideal_core;
@@ -415,23 +478,19 @@ public:
return affinity_mask;
}
- ThreadActivity GetActivity() const {
- return activity;
- }
-
- void SetActivity(ThreadActivity value);
+ ResultCode SetActivity(ThreadActivity value);
/// Sleeps this thread for the given amount of nanoseconds.
- void Sleep(s64 nanoseconds);
+ ResultCode Sleep(s64 nanoseconds);
/// Yields this thread without rebalancing loads.
- bool YieldSimple();
+ std::pair<ResultCode, bool> YieldSimple();
/// Yields this thread and does a load rebalancing.
- bool YieldAndBalanceLoad();
+ std::pair<ResultCode, bool> YieldAndBalanceLoad();
/// Yields this thread and if the core is left idle, loads are rebalanced
- bool YieldAndWaitForLoadBalancing();
+ std::pair<ResultCode, bool> YieldAndWaitForLoadBalancing();
void IncrementYieldCount() {
yield_count++;
@@ -446,6 +505,10 @@ public:
static_cast<u32>(ThreadSchedMasks::LowMask));
}
+ bool IsRunnable() const {
+ return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable);
+ }
+
bool IsRunning() const {
return is_running;
}
@@ -466,17 +529,65 @@ public:
return global_handle;
}
+ bool IsWaitingForArbitration() const {
+ return waiting_for_arbitration;
+ }
+
+ void WaitForArbitration(bool set) {
+ waiting_for_arbitration = set;
+ }
+
+ bool IsWaitingSync() const {
+ return is_waiting_on_sync;
+ }
+
+ void SetWaitingSync(bool is_waiting) {
+ is_waiting_on_sync = is_waiting;
+ }
+
+ bool IsPendingTermination() const {
+ return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited;
+ }
+
+ bool IsPaused() const {
+ return pausing_state != 0;
+ }
+
+ bool IsContinuousOnSVC() const {
+ return is_continuous_on_svc;
+ }
+
+ void SetContinuousOnSVC(bool is_continuous) {
+ is_continuous_on_svc = is_continuous;
+ }
+
+ bool IsPhantomMode() const {
+ return is_phantom_mode;
+ }
+
+ void SetPhantomMode(bool phantom) {
+ is_phantom_mode = phantom;
+ }
+
+ bool HasExited() const {
+ return has_exited;
+ }
+
private:
+ friend class GlobalScheduler;
+ friend class Scheduler;
+
void SetSchedulingStatus(ThreadSchedStatus new_status);
- void SetCurrentPriority(u32 new_priority);
- ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
+ void AddSchedulingFlag(ThreadSchedFlags flag);
+ void RemoveSchedulingFlag(ThreadSchedFlags flag);
- void AdjustSchedulingOnStatus(u32 old_flags);
- void AdjustSchedulingOnPriority(u32 old_priority);
- void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
+ void SetCurrentPriority(u32 new_priority);
+ Common::SpinLock context_guard{};
ThreadContext32 context_32{};
ThreadContext64 context_64{};
+ std::unique_ptr<Core::ARM_Interface> arm_interface{};
+ std::shared_ptr<Common::Fiber> host_context{};
u64 thread_id = 0;
@@ -485,6 +596,8 @@ private:
VAddr entry_point = 0;
VAddr stack_top = 0;
+ ThreadType type;
+
/// Nominal thread priority, as set by the emulated application.
/// The nominal priority is the thread priority without priority
/// inheritance taken into account.
@@ -509,7 +622,10 @@ private:
/// Objects that the thread is waiting on, in the same order as they were
/// passed to WaitSynchronization.
- ThreadSynchronizationObjects wait_objects;
+ ThreadSynchronizationObjects* wait_objects;
+
+ SynchronizationObject* signaling_object;
+ ResultCode signaling_result{RESULT_SUCCESS};
/// List of threads that are waiting for a mutex that is held by this thread.
MutexWaitingThreads wait_mutex_threads;
@@ -526,36 +642,40 @@ private:
/// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
+ bool waiting_for_arbitration{};
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle global_handle = 0;
- /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
- /// was waiting via WaitSynchronization then the object will be the last object that became
- /// available. In case of a timeout, the object will be nullptr.
- WakeupCallback wakeup_callback;
+ /// Callback for HLE Events
+ HLECallback hle_callback;
+ Handle hle_time_event;
+ SynchronizationObject* hle_object;
Scheduler* scheduler = nullptr;
u32 ideal_core{0xFFFFFFFF};
u64 affinity_mask{0x1};
- ThreadActivity activity = ThreadActivity::Normal;
-
s32 ideal_core_override = -1;
u64 affinity_mask_override = 0x1;
u32 affinity_override_count = 0;
u32 scheduling_state = 0;
+ u32 pausing_state = 0;
bool is_running = false;
+ bool is_waiting_on_sync = false;
bool is_sync_cancelled = false;
+ bool is_continuous_on_svc = false;
+
+ bool will_be_terminated = false;
+ bool is_phantom_mode = false;
+ bool has_exited = false;
+
+ bool was_running = false;
+
std::string name;
};
-/**
- * Gets the current thread
- */
-Thread* GetCurrentThread();
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 21b290468..95f2446c9 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -8,30 +8,38 @@
#include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
-TimeManager::TimeManager(Core::System& system) : system{system} {
+TimeManager::TimeManager(Core::System& system_) : system{system_} {
time_manager_event_type = Core::Timing::CreateEvent(
- "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
- Handle proper_handle = static_cast<Handle>(thread_handle);
- std::shared_ptr<Thread> thread =
- this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
- thread->ResumeFromWait();
+ "Kernel::TimeManagerCallback",
+ [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
+ const SchedulerLock lock(system.Kernel());
+ const auto proper_handle = static_cast<Handle>(thread_handle);
+ if (cancelled_events[proper_handle]) {
+ return;
+ }
+ auto thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
+ thread->OnWakeUp();
});
}
void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
+ event_handle = timetask->GetGlobalHandle();
if (nanoseconds > 0) {
ASSERT(timetask);
- event_handle = timetask->GetGlobalHandle();
- const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
- system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle);
+ ASSERT(timetask->GetStatus() != ThreadStatus::Ready);
+ ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex);
+ system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
+ time_manager_event_type, event_handle);
} else {
event_handle = InvalidHandle;
}
+ cancelled_events[event_handle] = false;
}
void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
@@ -39,6 +47,12 @@ void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
return;
}
system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
+ cancelled_events[event_handle] = true;
+}
+
+void TimeManager::CancelTimeEvent(Thread* time_task) {
+ Handle event_handle = time_task->GetGlobalHandle();
+ UnscheduleTimeEvent(event_handle);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
index eaec486d1..307a18765 100644
--- a/src/core/hle/kernel/time_manager.h
+++ b/src/core/hle/kernel/time_manager.h
@@ -5,6 +5,7 @@
#pragma once
#include <memory>
+#include <unordered_map>
#include "core/hle/kernel/object.h"
@@ -35,9 +36,12 @@ public:
/// Unschedule an existing time event
void UnscheduleTimeEvent(Handle event_handle);
+ void CancelTimeEvent(Thread* time_task);
+
private:
Core::System& system;
std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
+ std::unordered_map<Handle, bool> cancelled_events;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
index f2d3f8b49..765f408c3 100644
--- a/src/core/hle/kernel/transfer_memory.cpp
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -2,17 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
-TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory)
+TransferMemory::TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory)
: Object{kernel}, memory{memory} {}
TransferMemory::~TransferMemory() {
@@ -20,14 +19,15 @@ TransferMemory::~TransferMemory() {
Reset();
}
-std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory,
- VAddr base_address, u64 size,
- MemoryPermission permissions) {
+std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel,
+ Core::Memory::Memory& memory,
+ VAddr base_address, std::size_t size,
+ Memory::MemoryPermission permissions) {
std::shared_ptr<TransferMemory> transfer_memory{
std::make_shared<TransferMemory>(kernel, memory)};
transfer_memory->base_address = base_address;
- transfer_memory->memory_size = size;
+ transfer_memory->size = size;
transfer_memory->owner_permissions = permissions;
transfer_memory->owner_process = kernel.CurrentProcess();
@@ -38,98 +38,12 @@ const u8* TransferMemory::GetPointer() const {
return memory.GetPointer(base_address);
}
-u64 TransferMemory::GetSize() const {
- return memory_size;
-}
-
-ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) {
- if (memory_size != size) {
- return ERR_INVALID_SIZE;
- }
-
- if (owner_permissions != permissions) {
- return ERR_INVALID_STATE;
- }
-
- if (is_mapped) {
- return ERR_INVALID_STATE;
- }
-
- backing_block = std::make_shared<PhysicalMemory>(size);
-
- const auto map_state = owner_permissions == MemoryPermission::None
- ? MemoryState::TransferMemoryIsolated
- : MemoryState::TransferMemory;
- auto& vm_manager = owner_process->VMManager();
- const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state);
- if (map_result.Failed()) {
- return map_result.Code();
- }
-
- is_mapped = true;
- return RESULT_SUCCESS;
-}
-
ResultCode TransferMemory::Reserve() {
- auto& vm_manager{owner_process->VMManager()};
- const auto check_range_result{vm_manager.CheckRangeState(
- base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
- MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All,
- VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None,
- MemoryAttribute::IpcAndDeviceMapped)};
-
- if (check_range_result.Failed()) {
- return check_range_result.Code();
- }
-
- auto [state_, permissions_, attribute] = *check_range_result;
-
- if (const auto result{vm_manager.ReprotectRange(
- base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))};
- result.IsError()) {
- return result;
- }
-
- return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
- attribute | MemoryAttribute::Locked);
+ return owner_process->PageTable().ReserveTransferMemory(base_address, size, owner_permissions);
}
ResultCode TransferMemory::Reset() {
- auto& vm_manager{owner_process->VMManager()};
- if (const auto result{vm_manager.CheckRangeState(
- base_address, memory_size,
- MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
- MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None,
- VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked,
- MemoryAttribute::IpcAndDeviceMapped)};
- result.Failed()) {
- return result.Code();
- }
-
- if (const auto result{
- vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)};
- result.IsError()) {
- return result;
- }
-
- return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
- MemoryAttribute::None);
-}
-
-ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
- if (memory_size != size) {
- return ERR_INVALID_SIZE;
- }
-
- auto& vm_manager = owner_process->VMManager();
- const auto result = vm_manager.UnmapRange(address, size);
-
- if (result.IsError()) {
- return result;
- }
-
- is_mapped = false;
- return RESULT_SUCCESS;
+ return owner_process->PageTable().ResetTransferMemory(base_address, size);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
index 6e388536a..05e9f7464 100644
--- a/src/core/hle/kernel/transfer_memory.h
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -6,12 +6,13 @@
#include <memory>
+#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/physical_memory.h"
union ResultCode;
-namespace Memory {
+namespace Core::Memory {
class Memory;
}
@@ -20,8 +21,6 @@ namespace Kernel {
class KernelCore;
class Process;
-enum class MemoryPermission : u32;
-
/// Defines the interface for transfer memory objects.
///
/// Transfer memory is typically used for the purpose of
@@ -30,14 +29,14 @@ enum class MemoryPermission : u32;
///
class TransferMemory final : public Object {
public:
- explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory);
+ explicit TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory);
~TransferMemory() override;
static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
- static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory,
- VAddr base_address, u64 size,
- MemoryPermission permissions);
+ static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Core::Memory::Memory& memory,
+ VAddr base_address, std::size_t size,
+ Memory::MemoryPermission permissions);
TransferMemory(const TransferMemory&) = delete;
TransferMemory& operator=(const TransferMemory&) = delete;
@@ -61,29 +60,9 @@ public:
const u8* GetPointer() const;
/// Gets the size of the memory backing this instance in bytes.
- u64 GetSize() const;
-
- /// Attempts to map transfer memory with the given range and memory permissions.
- ///
- /// @param address The base address to being mapping memory at.
- /// @param size The size of the memory to map, in bytes.
- /// @param permissions The memory permissions to check against when mapping memory.
- ///
- /// @pre The given address, size, and memory permissions must all match
- /// the same values that were given when creating the transfer memory
- /// instance.
- ///
- ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions);
-
- /// Unmaps the transfer memory with the given range
- ///
- /// @param address The base address to begin unmapping memory at.
- /// @param size The size of the memory to unmap, in bytes.
- ///
- /// @pre The given address and size must be the same as the ones used
- /// to create the transfer memory instance.
- ///
- ResultCode UnmapMemory(VAddr address, u64 size);
+ constexpr std::size_t GetSize() const {
+ return size;
+ }
/// Reserves the region to be used for the transfer memory, called after the transfer memory is
/// created.
@@ -94,25 +73,19 @@ public:
ResultCode Reset();
private:
- /// Memory block backing this instance.
- std::shared_ptr<PhysicalMemory> backing_block;
-
/// The base address for the memory managed by this instance.
- VAddr base_address = 0;
+ VAddr base_address{};
/// Size of the memory, in bytes, that this instance manages.
- u64 memory_size = 0;
+ std::size_t size{};
/// The memory permissions that are applied to this instance.
- MemoryPermission owner_permissions{};
+ Memory::MemoryPermission owner_permissions{};
/// The process that this transfer memory instance was created under.
- Process* owner_process = nullptr;
-
- /// Whether or not this transfer memory instance has mapped memory.
- bool is_mapped = false;
+ Process* owner_process{};
- Memory::Memory& memory;
+ Core::Memory::Memory& memory;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
deleted file mode 100644
index 024c22901..000000000
--- a/src/core/hle/kernel/vm_manager.cpp
+++ /dev/null
@@ -1,1175 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cstring>
-#include <iterator>
-#include <utility>
-#include "common/alignment.h"
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "common/memory_hook.h"
-#include "core/core.h"
-#include "core/file_sys/program_metadata.h"
-#include "core/hle/kernel/errors.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/resource_limit.h"
-#include "core/hle/kernel/vm_manager.h"
-#include "core/memory.h"
-
-namespace Kernel {
-namespace {
-const char* GetMemoryStateName(MemoryState state) {
- static constexpr const char* names[] = {
- "Unmapped", "Io",
- "Normal", "Code",
- "CodeData", "Heap",
- "Shared", "Unknown1",
- "ModuleCode", "ModuleCodeData",
- "IpcBuffer0", "Stack",
- "ThreadLocal", "TransferMemoryIsolated",
- "TransferMemory", "ProcessMemory",
- "Inaccessible", "IpcBuffer1",
- "IpcBuffer3", "KernelStack",
- };
-
- return names[ToSvcMemoryState(state)];
-}
-
-// 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;
-}
-} // Anonymous namespace
-
-bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
- ASSERT(base + size == next.base);
- if (permissions != next.permissions || state != next.state || attribute != next.attribute ||
- type != next.type) {
- return false;
- }
- if ((attribute & MemoryAttribute::DeviceMapped) == MemoryAttribute::DeviceMapped) {
- // TODO: Can device mapped memory be merged sanely?
- // Not merging it may cause inaccuracies versus hardware when memory layout is queried.
- return false;
- }
- if (type == VMAType::AllocatedMemoryBlock) {
- return true;
- }
- if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) {
- return false;
- }
- if (type == VMAType::MMIO && paddr + size != next.paddr) {
- return false;
- }
- return true;
-}
-
-VMManager::VMManager(Core::System& system) : system{system} {
- // Default to assuming a 39-bit address space. This way we have a sane
- // starting point with executables that don't provide metadata.
- Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
-}
-
-VMManager::~VMManager() = default;
-
-void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
- Clear();
-
- InitializeMemoryRegionRanges(type);
-
- page_table.Resize(address_space_width);
-
- // Initialize the map with a single free region covering the entire managed space.
- VirtualMemoryArea initial_vma;
- initial_vma.size = address_space_end;
- vma_map.emplace(initial_vma.base, initial_vma);
-
- UpdatePageTableForVMA(initial_vma);
-}
-
-VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
- if (target >= address_space_end) {
- return vma_map.end();
- } else {
- return std::prev(vma_map.upper_bound(target));
- }
-}
-
-bool VMManager::IsValidHandle(VMAHandle handle) const {
- return handle != vma_map.cend();
-}
-
-ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
- std::shared_ptr<PhysicalMemory> block,
- std::size_t offset, u64 size,
- MemoryState state, VMAPermission perm) {
- ASSERT(block != nullptr);
- ASSERT(offset + size <= block->size());
-
- // This is the appropriately sized VMA that will turn into our allocation.
- CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
- VirtualMemoryArea& final_vma = vma_handle->second;
- ASSERT(final_vma.size == size);
-
- final_vma.type = VMAType::AllocatedMemoryBlock;
- final_vma.permissions = perm;
- final_vma.state = state;
- final_vma.backing_block = std::move(block);
- final_vma.offset = offset;
- UpdatePageTableForVMA(final_vma);
-
- return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
-}
-
-ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u64 size,
- MemoryState state) {
- ASSERT(memory != nullptr);
-
- // This is the appropriately sized VMA that will turn into our allocation.
- CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
- VirtualMemoryArea& final_vma = vma_handle->second;
- ASSERT(final_vma.size == size);
-
- final_vma.type = VMAType::BackingMemory;
- final_vma.permissions = VMAPermission::ReadWrite;
- final_vma.state = state;
- final_vma.backing_memory = memory;
- UpdatePageTableForVMA(final_vma);
-
- return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
-}
-
-ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
- return FindFreeRegion(GetASLRRegionBaseAddress(), GetASLRRegionEndAddress(), size);
-}
-
-ResultVal<VAddr> VMManager::FindFreeRegion(VAddr begin, VAddr end, u64 size) const {
- ASSERT(begin < end);
- ASSERT(size <= end - begin);
-
- const VMAHandle vma_handle =
- std::find_if(vma_map.begin(), vma_map.end(), [begin, end, size](const auto& vma) {
- if (vma.second.type != VMAType::Free) {
- return false;
- }
- const VAddr vma_base = vma.second.base;
- const VAddr vma_end = vma_base + vma.second.size;
- const VAddr assumed_base = (begin < vma_base) ? vma_base : begin;
- const VAddr used_range = assumed_base + size;
-
- return vma_base <= assumed_base && assumed_base < used_range && used_range < end &&
- used_range <= vma_end;
- });
-
- if (vma_handle == vma_map.cend()) {
- // TODO(Subv): Find the correct error code here.
- return RESULT_UNKNOWN;
- }
-
- const VAddr target = std::max(begin, vma_handle->second.base);
- return MakeResult<VAddr>(target);
-}
-
-ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
- MemoryState state,
- Common::MemoryHookPointer mmio_handler) {
- // This is the appropriately sized VMA that will turn into our allocation.
- CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
- VirtualMemoryArea& final_vma = vma_handle->second;
- ASSERT(final_vma.size == size);
-
- final_vma.type = VMAType::MMIO;
- final_vma.permissions = VMAPermission::ReadWrite;
- final_vma.state = state;
- final_vma.paddr = paddr;
- final_vma.mmio_handler = std::move(mmio_handler);
- UpdatePageTableForVMA(final_vma);
-
- return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
-}
-
-VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
- VirtualMemoryArea& vma = vma_handle->second;
- vma.type = VMAType::Free;
- vma.permissions = VMAPermission::None;
- vma.state = MemoryState::Unmapped;
- vma.attribute = MemoryAttribute::None;
-
- vma.backing_block = nullptr;
- vma.offset = 0;
- vma.backing_memory = nullptr;
- vma.paddr = 0;
-
- UpdatePageTableForVMA(vma);
-
- return MergeAdjacent(vma_handle);
-}
-
-ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
- CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
- const VAddr target_end = target + size;
-
- const VMAIter end = vma_map.end();
- // The comparison against the end of the range must be done using addresses since VMAs can be
- // merged during this process, causing invalidation of the iterators.
- while (vma != end && vma->second.base < target_end) {
- vma = std::next(Unmap(vma));
- }
-
- ASSERT(FindVMA(target)->second.size >= size);
-
- return RESULT_SUCCESS;
-}
-
-VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) {
- VMAIter iter = StripIterConstness(vma_handle);
-
- VirtualMemoryArea& vma = iter->second;
- vma.permissions = new_perms;
- UpdatePageTableForVMA(vma);
-
- return MergeAdjacent(iter);
-}
-
-ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) {
- CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
- const VAddr target_end = target + size;
-
- const VMAIter end = vma_map.end();
- // The comparison against the end of the range must be done using addresses since VMAs can be
- // merged during this process, causing invalidation of the iterators.
- while (vma != end && vma->second.base < target_end) {
- vma = std::next(StripIterConstness(Reprotect(vma, new_perms)));
- }
-
- return RESULT_SUCCESS;
-}
-
-ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
- if (size > GetHeapRegionSize()) {
- return ERR_OUT_OF_MEMORY;
- }
-
- // No need to do any additional work if the heap is already the given size.
- if (size == GetCurrentHeapSize()) {
- return MakeResult(heap_region_base);
- }
-
- if (heap_memory == nullptr) {
- // Initialize heap
- heap_memory = std::make_shared<PhysicalMemory>(size);
- heap_end = heap_region_base + size;
- } else {
- UnmapRange(heap_region_base, GetCurrentHeapSize());
- }
-
- // If necessary, expand backing vector to cover new heap extents in
- // the case of allocating. Otherwise, shrink the backing memory,
- // if a smaller heap has been requested.
- heap_memory->resize(size);
- heap_memory->shrink_to_fit();
- RefreshMemoryBlockMappings(heap_memory.get());
-
- heap_end = heap_region_base + size;
- ASSERT(GetCurrentHeapSize() == heap_memory->size());
-
- const auto mapping_result =
- MapMemoryBlock(heap_region_base, heap_memory, 0, size, MemoryState::Heap);
- if (mapping_result.Failed()) {
- return mapping_result.Code();
- }
-
- return MakeResult<VAddr>(heap_region_base);
-}
-
-ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
- // Check how much memory we've already mapped.
- const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size);
- if (mapped_size_result.Failed()) {
- return mapped_size_result.Code();
- }
-
- // If we've already mapped the desired amount, return early.
- const std::size_t mapped_size = *mapped_size_result;
- if (mapped_size == size) {
- return RESULT_SUCCESS;
- }
-
- // Check that we can map the memory we want.
- const auto res_limit = system.CurrentProcess()->GetResourceLimit();
- const u64 physmem_remaining = res_limit->GetMaxResourceValue(ResourceType::PhysicalMemory) -
- res_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory);
- if (physmem_remaining < (size - mapped_size)) {
- return ERR_RESOURCE_LIMIT_EXCEEDED;
- }
-
- // Keep track of the memory regions we unmap.
- std::vector<std::pair<u64, u64>> mapped_regions;
- ResultCode result = RESULT_SUCCESS;
-
- // Iterate, trying to map memory.
- {
- const auto end_addr = target + size;
- const auto last_addr = end_addr - 1;
- VAddr cur_addr = target;
-
- auto iter = FindVMA(target);
- ASSERT(iter != vma_map.end());
-
- while (true) {
- const auto& vma = iter->second;
- const auto vma_start = vma.base;
- const auto vma_end = vma_start + vma.size;
- const auto vma_last = vma_end - 1;
-
- // Map the memory block
- const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
- if (vma.state == MemoryState::Unmapped) {
- const auto map_res =
- MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size), 0,
- map_size, MemoryState::Heap, VMAPermission::ReadWrite);
- result = map_res.Code();
- if (result.IsError()) {
- break;
- }
-
- mapped_regions.emplace_back(cur_addr, map_size);
- }
-
- // Break once we hit the end of the range.
- if (last_addr <= vma_last) {
- break;
- }
-
- // Advance to the next block.
- cur_addr = vma_end;
- iter = FindVMA(cur_addr);
- ASSERT(iter != vma_map.end());
- }
- }
-
- // If we failed, unmap memory.
- if (result.IsError()) {
- for (const auto [unmap_address, unmap_size] : mapped_regions) {
- ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(),
- "Failed to unmap memory range.");
- }
-
- return result;
- }
-
- // Update amount of mapped physical memory.
- physical_memory_mapped += size - mapped_size;
-
- return RESULT_SUCCESS;
-}
-
-ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
- // Check how much memory is currently mapped.
- const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size);
- if (mapped_size_result.Failed()) {
- return mapped_size_result.Code();
- }
-
- // If we've already unmapped all the memory, return early.
- const std::size_t mapped_size = *mapped_size_result;
- if (mapped_size == 0) {
- return RESULT_SUCCESS;
- }
-
- // Keep track of the memory regions we unmap.
- std::vector<std::pair<u64, u64>> unmapped_regions;
- ResultCode result = RESULT_SUCCESS;
-
- // Try to unmap regions.
- {
- const auto end_addr = target + size;
- const auto last_addr = end_addr - 1;
- VAddr cur_addr = target;
-
- auto iter = FindVMA(target);
- ASSERT(iter != vma_map.end());
-
- while (true) {
- const auto& vma = iter->second;
- const auto vma_start = vma.base;
- const auto vma_end = vma_start + vma.size;
- const auto vma_last = vma_end - 1;
-
- // Unmap the memory block
- const auto unmap_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
- if (vma.state == MemoryState::Heap) {
- result = UnmapRange(cur_addr, unmap_size);
- if (result.IsError()) {
- break;
- }
-
- unmapped_regions.emplace_back(cur_addr, unmap_size);
- }
-
- // Break once we hit the end of the range.
- if (last_addr <= vma_last) {
- break;
- }
-
- // Advance to the next block.
- cur_addr = vma_end;
- iter = FindVMA(cur_addr);
- ASSERT(iter != vma_map.end());
- }
- }
-
- // If we failed, re-map regions.
- // TODO: Preserve memory contents?
- if (result.IsError()) {
- for (const auto [map_address, map_size] : unmapped_regions) {
- const auto remap_res =
- MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size), 0, map_size,
- MemoryState::Heap, VMAPermission::None);
- ASSERT_MSG(remap_res.Succeeded(), "Failed to remap a memory block.");
- }
-
- return result;
- }
-
- // Update mapped amount
- physical_memory_mapped -= mapped_size;
-
- return RESULT_SUCCESS;
-}
-
-ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
- constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
- const auto src_check_result = CheckRangeState(
- src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All,
- VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
-
- if (src_check_result.Failed()) {
- return src_check_result.Code();
- }
-
- const auto mirror_result =
- MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode);
- if (mirror_result.IsError()) {
- return mirror_result;
- }
-
- // Ensure we lock the source memory region.
- const auto src_vma_result = CarveVMARange(src_address, size);
- if (src_vma_result.Failed()) {
- return src_vma_result.Code();
- }
- auto src_vma_iter = *src_vma_result;
- src_vma_iter->second.attribute = MemoryAttribute::Locked;
- Reprotect(src_vma_iter, VMAPermission::Read);
-
- // The destination memory region is fine as is, however we need to make it read-only.
- return ReprotectRange(dst_address, size, VMAPermission::Read);
-}
-
-ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
- constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
- const auto src_check_result = CheckRangeState(
- src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None,
- VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute);
-
- if (src_check_result.Failed()) {
- return src_check_result.Code();
- }
-
- // Yes, the kernel only checks the first page of the region.
- const auto dst_check_result =
- CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule,
- MemoryState::FlagModule, VMAPermission::None, VMAPermission::None,
- MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
-
- if (dst_check_result.Failed()) {
- return dst_check_result.Code();
- }
-
- const auto dst_memory_state = std::get<MemoryState>(*dst_check_result);
- const auto dst_contiguous_check_result = CheckRangeState(
- dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None,
- VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
-
- if (dst_contiguous_check_result.Failed()) {
- return dst_contiguous_check_result.Code();
- }
-
- const auto unmap_result = UnmapRange(dst_address, size);
- if (unmap_result.IsError()) {
- return unmap_result;
- }
-
- // With the mirrored portion unmapped, restore the original region's traits.
- const auto src_vma_result = CarveVMARange(src_address, size);
- if (src_vma_result.Failed()) {
- return src_vma_result.Code();
- }
- auto src_vma_iter = *src_vma_result;
- src_vma_iter->second.state = MemoryState::Heap;
- src_vma_iter->second.attribute = MemoryAttribute::None;
- Reprotect(src_vma_iter, VMAPermission::ReadWrite);
-
- if (dst_memory_state == MemoryState::ModuleCode) {
- system.InvalidateCpuInstructionCaches();
- }
-
- return unmap_result;
-}
-
-MemoryInfo VMManager::QueryMemory(VAddr address) const {
- const auto vma = FindVMA(address);
- MemoryInfo memory_info{};
-
- if (IsValidHandle(vma)) {
- memory_info.base_address = vma->second.base;
- memory_info.attributes = ToSvcMemoryAttribute(vma->second.attribute);
- memory_info.permission = static_cast<u32>(vma->second.permissions);
- memory_info.size = vma->second.size;
- memory_info.state = ToSvcMemoryState(vma->second.state);
- } else {
- memory_info.base_address = address_space_end;
- memory_info.permission = static_cast<u32>(VMAPermission::None);
- memory_info.size = 0 - address_space_end;
- memory_info.state = static_cast<u32>(MemoryState::Inaccessible);
- }
-
- return memory_info;
-}
-
-ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
- MemoryAttribute attribute) {
- constexpr auto ignore_mask =
- MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked;
- constexpr auto attribute_mask = ~ignore_mask;
-
- const auto result = CheckRangeState(
- address, size, MemoryState::FlagUncached, MemoryState::FlagUncached, VMAPermission::None,
- VMAPermission::None, attribute_mask, MemoryAttribute::None, ignore_mask);
-
- if (result.Failed()) {
- return result.Code();
- }
-
- const auto [prev_state, prev_permissions, prev_attributes] = *result;
- const auto new_attribute = (prev_attributes & ~mask) | (mask & attribute);
-
- const auto carve_result = CarveVMARange(address, size);
- if (carve_result.Failed()) {
- return carve_result.Code();
- }
-
- auto vma_iter = *carve_result;
- vma_iter->second.attribute = new_attribute;
-
- MergeAdjacent(vma_iter);
- return RESULT_SUCCESS;
-}
-
-ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
- const auto vma = FindVMA(src_addr);
-
- ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
- ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
-
- // The returned VMA might be a bigger one encompassing the desired address.
- const auto vma_offset = src_addr - vma->first;
- ASSERT_MSG(vma_offset + size <= vma->second.size,
- "Shared memory exceeds bounds of mapped block");
-
- const std::shared_ptr<PhysicalMemory>& backing_block = vma->second.backing_block;
- const std::size_t backing_block_offset = vma->second.offset + vma_offset;
-
- CASCADE_RESULT(auto new_vma,
- MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
- // Protect mirror with permissions from old region
- Reprotect(new_vma, vma->second.permissions);
- // Remove permissions from old region
- ReprotectRange(src_addr, size, VMAPermission::None);
-
- return RESULT_SUCCESS;
-}
-
-void VMManager::RefreshMemoryBlockMappings(const PhysicalMemory* block) {
- // If this ever proves to have a noticeable performance impact, allow users of the function to
- // specify a specific range of addresses to limit the scan to.
- for (const auto& p : vma_map) {
- const VirtualMemoryArea& vma = p.second;
- if (block == vma.backing_block.get()) {
- UpdatePageTableForVMA(vma);
- }
- }
-}
-
-void VMManager::LogLayout() const {
- for (const auto& p : vma_map) {
- const VirtualMemoryArea& vma = p.second;
- LOG_DEBUG(Kernel, "{:016X} - {:016X} size: {:016X} {}{}{} {}", vma.base,
- vma.base + vma.size, vma.size,
- (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
- (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
- (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-',
- GetMemoryStateName(vma.state));
- }
-}
-
-VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) {
- // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given
- // non-const access to its container.
- return vma_map.erase(iter, iter); // Erases an empty range of elements
-}
-
-ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) {
- ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size);
- ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", base);
-
- VMAIter vma_handle = StripIterConstness(FindVMA(base));
- if (vma_handle == vma_map.end()) {
- // Target address is outside the range managed by the kernel
- return ERR_INVALID_ADDRESS;
- }
-
- const VirtualMemoryArea& vma = vma_handle->second;
- if (vma.type != VMAType::Free) {
- // Region is already allocated
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- const VAddr start_in_vma = base - vma.base;
- const VAddr end_in_vma = start_in_vma + size;
-
- if (end_in_vma > vma.size) {
- // Requested allocation doesn't fit inside VMA
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if (end_in_vma != vma.size) {
- // Split VMA at the end of the allocated region
- SplitVMA(vma_handle, end_in_vma);
- }
- if (start_in_vma != 0) {
- // Split VMA at the start of the allocated region
- vma_handle = SplitVMA(vma_handle, start_in_vma);
- }
-
- return MakeResult<VMAIter>(vma_handle);
-}
-
-ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) {
- ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size);
- ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", target);
-
- const VAddr target_end = target + size;
- ASSERT(target_end >= target);
- ASSERT(target_end <= address_space_end);
- ASSERT(size > 0);
-
- VMAIter begin_vma = StripIterConstness(FindVMA(target));
- const VMAIter i_end = vma_map.lower_bound(target_end);
- if (std::any_of(begin_vma, i_end,
- [](const auto& entry) { return entry.second.type == VMAType::Free; })) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if (target != begin_vma->second.base) {
- begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base);
- }
-
- VMAIter end_vma = StripIterConstness(FindVMA(target_end));
- if (end_vma != vma_map.end() && target_end != end_vma->second.base) {
- end_vma = SplitVMA(end_vma, target_end - end_vma->second.base);
- }
-
- return MakeResult<VMAIter>(begin_vma);
-}
-
-VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
- VirtualMemoryArea& old_vma = vma_handle->second;
- VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA
-
- // For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably
- // a bug. This restriction might be removed later.
- ASSERT(offset_in_vma < old_vma.size);
- ASSERT(offset_in_vma > 0);
-
- old_vma.size = offset_in_vma;
- new_vma.base += offset_in_vma;
- new_vma.size -= offset_in_vma;
-
- switch (new_vma.type) {
- case VMAType::Free:
- break;
- case VMAType::AllocatedMemoryBlock:
- new_vma.offset += offset_in_vma;
- break;
- case VMAType::BackingMemory:
- new_vma.backing_memory += offset_in_vma;
- break;
- case VMAType::MMIO:
- new_vma.paddr += offset_in_vma;
- break;
- }
-
- ASSERT(old_vma.CanBeMergedWith(new_vma));
-
- return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma);
-}
-
-VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
- const VMAIter next_vma = std::next(iter);
- if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
- MergeAdjacentVMA(iter->second, next_vma->second);
- vma_map.erase(next_vma);
- }
-
- if (iter != vma_map.begin()) {
- VMAIter prev_vma = std::prev(iter);
- if (prev_vma->second.CanBeMergedWith(iter->second)) {
- MergeAdjacentVMA(prev_vma->second, iter->second);
- vma_map.erase(iter);
- iter = prev_vma;
- }
- }
-
- return iter;
-}
-
-void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right) {
- ASSERT(left.CanBeMergedWith(right));
-
- // Always merge allocated memory blocks, even when they don't share the same backing block.
- if (left.type == VMAType::AllocatedMemoryBlock &&
- (left.backing_block != right.backing_block || left.offset + left.size != right.offset)) {
-
- // Check if we can save work.
- if (left.offset == 0 && left.size == left.backing_block->size()) {
- // Fast case: left is an entire backing block.
- left.backing_block->resize(left.size + right.size);
- std::memcpy(left.backing_block->data() + left.size,
- right.backing_block->data() + right.offset, right.size);
- } else {
- // Slow case: make a new memory block for left and right.
- auto new_memory = std::make_shared<PhysicalMemory>();
- new_memory->resize(left.size + right.size);
- std::memcpy(new_memory->data(), left.backing_block->data() + left.offset, left.size);
- std::memcpy(new_memory->data() + left.size, right.backing_block->data() + right.offset,
- right.size);
-
- left.backing_block = std::move(new_memory);
- left.offset = 0;
- }
-
- // Page table update is needed, because backing memory changed.
- left.size += right.size;
- UpdatePageTableForVMA(left);
- } else {
- // Just update the size.
- left.size += right.size;
- }
-}
-
-void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
- auto& memory = system.Memory();
-
- switch (vma.type) {
- case VMAType::Free:
- memory.UnmapRegion(page_table, vma.base, vma.size);
- break;
- case VMAType::AllocatedMemoryBlock:
- memory.MapMemoryRegion(page_table, vma.base, vma.size, *vma.backing_block, vma.offset);
- break;
- case VMAType::BackingMemory:
- memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
- break;
- case VMAType::MMIO:
- memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
- break;
- }
-}
-
-void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) {
- u64 map_region_size = 0;
- u64 heap_region_size = 0;
- u64 stack_region_size = 0;
- u64 tls_io_region_size = 0;
-
- u64 stack_and_tls_io_end = 0;
-
- 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;
- 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;
- }
- stack_and_tls_io_end = 0x40000000;
- 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;
- stack_and_tls_io_end = 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;
- stack_region_size = 0x80000000;
- tls_io_region_size = 0x1000000000;
- break;
- default:
- UNREACHABLE_MSG("Invalid address space type specified: {}", static_cast<u32>(type));
- return;
- }
-
- const u64 stack_and_tls_io_begin = aslr_region_base;
-
- address_space_base = 0;
- address_space_end = 1ULL << address_space_width;
-
- map_region_base = code_region_end;
- map_region_end = map_region_base + map_region_size;
-
- heap_region_base = map_region_end;
- heap_region_end = heap_region_base + heap_region_size;
- heap_end = heap_region_base;
-
- stack_region_base = heap_region_end;
- stack_region_end = stack_region_base + stack_region_size;
-
- tls_io_region_base = stack_region_end;
- tls_io_region_end = tls_io_region_base + tls_io_region_size;
-
- if (stack_region_size == 0) {
- stack_region_base = stack_and_tls_io_begin;
- stack_region_end = stack_and_tls_io_end;
- }
-
- if (tls_io_region_size == 0) {
- tls_io_region_base = stack_and_tls_io_begin;
- tls_io_region_end = stack_and_tls_io_end;
- }
-}
-
-void VMManager::Clear() {
- ClearVMAMap();
- ClearPageTable();
-}
-
-void VMManager::ClearVMAMap() {
- vma_map.clear();
-}
-
-void VMManager::ClearPageTable() {
- std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
- page_table.special_regions.clear();
- std::fill(page_table.attributes.begin(), page_table.attributes.end(),
- Common::PageType::Unmapped);
-}
-
-VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
- MemoryState state, VMAPermission permission_mask,
- VMAPermission permissions,
- MemoryAttribute attribute_mask,
- MemoryAttribute attribute,
- MemoryAttribute ignore_mask) const {
- auto iter = FindVMA(address);
-
- // If we don't have a valid VMA handle at this point, then it means this is
- // being called with an address outside of the address space, which is definitely
- // indicative of a bug, as this function only operates on mapped memory regions.
- DEBUG_ASSERT(IsValidHandle(iter));
-
- const VAddr end_address = address + size - 1;
- const MemoryAttribute initial_attributes = iter->second.attribute;
- const VMAPermission initial_permissions = iter->second.permissions;
- const MemoryState initial_state = iter->second.state;
-
- while (true) {
- // The iterator should be valid throughout the traversal. Hitting the end of
- // the mapped VMA regions is unquestionably indicative of a bug.
- DEBUG_ASSERT(IsValidHandle(iter));
-
- const auto& vma = iter->second;
-
- if (vma.state != initial_state) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if ((vma.state & state_mask) != state) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if (vma.permissions != initial_permissions) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if ((vma.permissions & permission_mask) != permissions) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if ((vma.attribute | ignore_mask) != (initial_attributes | ignore_mask)) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if ((vma.attribute & attribute_mask) != attribute) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if (end_address <= vma.EndAddress()) {
- break;
- }
-
- ++iter;
- }
-
- return MakeResult(
- std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
-}
-
-ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
- std::size_t size) const {
- const VAddr end_addr = address + size;
- const VAddr last_addr = end_addr - 1;
- std::size_t mapped_size = 0;
-
- VAddr cur_addr = address;
- auto iter = FindVMA(cur_addr);
- ASSERT(iter != vma_map.end());
-
- while (true) {
- const auto& vma = iter->second;
- const VAddr vma_start = vma.base;
- const VAddr vma_end = vma_start + vma.size;
- const VAddr vma_last = vma_end - 1;
-
- // Add size if relevant.
- if (vma.state != MemoryState::Unmapped) {
- mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
- }
-
- // Break once we hit the end of the range.
- if (last_addr <= vma_last) {
- break;
- }
-
- // Advance to the next block.
- cur_addr = vma_end;
- iter = std::next(iter);
- ASSERT(iter != vma_map.end());
- }
-
- return MakeResult(mapped_size);
-}
-
-ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
- std::size_t size) const {
- const VAddr end_addr = address + size;
- const VAddr last_addr = end_addr - 1;
- std::size_t mapped_size = 0;
-
- VAddr cur_addr = address;
- auto iter = FindVMA(cur_addr);
- ASSERT(iter != vma_map.end());
-
- while (true) {
- const auto& vma = iter->second;
- const auto vma_start = vma.base;
- const auto vma_end = vma_start + vma.size;
- const auto vma_last = vma_end - 1;
- const auto state = vma.state;
- const auto attr = vma.attribute;
-
- // Memory within region must be free or mapped heap.
- if (!((state == MemoryState::Heap && attr == MemoryAttribute::None) ||
- (state == MemoryState::Unmapped))) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- // Add size if relevant.
- if (state != MemoryState::Unmapped) {
- mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
- }
-
- // Break once we hit the end of the range.
- if (last_addr <= vma_last) {
- break;
- }
-
- // Advance to the next block.
- cur_addr = vma_end;
- iter = std::next(iter);
- ASSERT(iter != vma_map.end());
- }
-
- return MakeResult(mapped_size);
-}
-
-u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
- LOG_WARNING(Kernel, "(STUBBED) called");
- return 0xF8000000;
-}
-
-VAddr VMManager::GetAddressSpaceBaseAddress() const {
- return address_space_base;
-}
-
-VAddr VMManager::GetAddressSpaceEndAddress() const {
- return address_space_end;
-}
-
-u64 VMManager::GetAddressSpaceSize() const {
- return address_space_end - address_space_base;
-}
-
-u64 VMManager::GetAddressSpaceWidth() const {
- return address_space_width;
-}
-
-bool VMManager::IsWithinAddressSpace(VAddr address, u64 size) const {
- return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(),
- GetAddressSpaceEndAddress());
-}
-
-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;
-}
-
-VAddr VMManager::GetCodeRegionEndAddress() const {
- return code_region_end;
-}
-
-u64 VMManager::GetCodeRegionSize() const {
- return code_region_end - code_region_base;
-}
-
-bool VMManager::IsWithinCodeRegion(VAddr address, u64 size) const {
- return IsInsideAddressRange(address, size, GetCodeRegionBaseAddress(),
- GetCodeRegionEndAddress());
-}
-
-VAddr VMManager::GetHeapRegionBaseAddress() const {
- return heap_region_base;
-}
-
-VAddr VMManager::GetHeapRegionEndAddress() const {
- return heap_region_end;
-}
-
-u64 VMManager::GetHeapRegionSize() const {
- return heap_region_end - heap_region_base;
-}
-
-u64 VMManager::GetCurrentHeapSize() const {
- return heap_end - heap_region_base;
-}
-
-bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
- return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
- GetHeapRegionEndAddress());
-}
-
-VAddr VMManager::GetMapRegionBaseAddress() const {
- return map_region_base;
-}
-
-VAddr VMManager::GetMapRegionEndAddress() const {
- return map_region_end;
-}
-
-u64 VMManager::GetMapRegionSize() const {
- return map_region_end - map_region_base;
-}
-
-bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const {
- return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
-}
-
-VAddr VMManager::GetStackRegionBaseAddress() const {
- return stack_region_base;
-}
-
-VAddr VMManager::GetStackRegionEndAddress() const {
- return stack_region_end;
-}
-
-u64 VMManager::GetStackRegionSize() const {
- return stack_region_end - stack_region_base;
-}
-
-bool VMManager::IsWithinStackRegion(VAddr address, u64 size) const {
- return IsInsideAddressRange(address, size, GetStackRegionBaseAddress(),
- GetStackRegionEndAddress());
-}
-
-VAddr VMManager::GetTLSIORegionBaseAddress() const {
- return tls_io_region_base;
-}
-
-VAddr VMManager::GetTLSIORegionEndAddress() const {
- return tls_io_region_end;
-}
-
-u64 VMManager::GetTLSIORegionSize() const {
- return tls_io_region_end - tls_io_region_base;
-}
-
-bool VMManager::IsWithinTLSIORegion(VAddr address, u64 size) const {
- return IsInsideAddressRange(address, size, GetTLSIORegionBaseAddress(),
- GetTLSIORegionEndAddress());
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
deleted file mode 100644
index 90b4b006a..000000000
--- a/src/core/hle/kernel/vm_manager.h
+++ /dev/null
@@ -1,796 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <map>
-#include <memory>
-#include <tuple>
-#include <vector>
-#include "common/common_types.h"
-#include "common/memory_hook.h"
-#include "common/page_table.h"
-#include "core/hle/kernel/physical_memory.h"
-#include "core/hle/result.h"
-#include "core/memory.h"
-
-namespace Core {
-class System;
-}
-
-namespace FileSys {
-enum class ProgramAddressSpaceType : u8;
-}
-
-namespace Kernel {
-
-enum class VMAType : u8 {
- /// VMA represents an unmapped region of the address space.
- Free,
- /// VMA is backed by a ref-counted allocate memory block.
- AllocatedMemoryBlock,
- /// VMA is backed by a raw, unmanaged pointer.
- BackingMemory,
- /// VMA is mapped to MMIO registers at a fixed PAddr.
- MMIO,
- // TODO(yuriks): Implement MemoryAlias to support MAP/UNMAP
-};
-
-/// Permissions for mapped memory blocks
-enum class VMAPermission : u8 {
- None = 0,
- Read = 1,
- Write = 2,
- Execute = 4,
-
- ReadWrite = Read | Write,
- ReadExecute = Read | Execute,
- WriteExecute = Write | Execute,
- ReadWriteExecute = Read | Write | Execute,
-
- // Used as a wildcard when checking permissions across memory ranges
- All = 0xFF,
-};
-
-constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
- return static_cast<VMAPermission>(u32(lhs) | u32(rhs));
-}
-
-constexpr VMAPermission operator&(VMAPermission lhs, VMAPermission rhs) {
- return static_cast<VMAPermission>(u32(lhs) & u32(rhs));
-}
-
-constexpr VMAPermission operator^(VMAPermission lhs, VMAPermission rhs) {
- return static_cast<VMAPermission>(u32(lhs) ^ u32(rhs));
-}
-
-constexpr VMAPermission operator~(VMAPermission permission) {
- return static_cast<VMAPermission>(~u32(permission));
-}
-
-constexpr VMAPermission& operator|=(VMAPermission& lhs, VMAPermission rhs) {
- lhs = lhs | rhs;
- return lhs;
-}
-
-constexpr VMAPermission& operator&=(VMAPermission& lhs, VMAPermission rhs) {
- lhs = lhs & rhs;
- return lhs;
-}
-
-constexpr VMAPermission& operator^=(VMAPermission& lhs, VMAPermission rhs) {
- lhs = lhs ^ rhs;
- return lhs;
-}
-
-/// Attribute flags that can be applied to a VMA
-enum class MemoryAttribute : u32 {
- Mask = 0xFF,
-
- /// No particular qualities
- None = 0,
- /// Memory locked/borrowed for use. e.g. This would be used by transfer memory.
- Locked = 1,
- /// Memory locked for use by IPC-related internals.
- LockedForIPC = 2,
- /// Mapped as part of the device address space.
- DeviceMapped = 4,
- /// Uncached memory
- Uncached = 8,
-
- IpcAndDeviceMapped = LockedForIPC | DeviceMapped,
-};
-
-constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) {
- return static_cast<MemoryAttribute>(u32(lhs) | u32(rhs));
-}
-
-constexpr MemoryAttribute operator&(MemoryAttribute lhs, MemoryAttribute rhs) {
- return static_cast<MemoryAttribute>(u32(lhs) & u32(rhs));
-}
-
-constexpr MemoryAttribute operator^(MemoryAttribute lhs, MemoryAttribute rhs) {
- return static_cast<MemoryAttribute>(u32(lhs) ^ u32(rhs));
-}
-
-constexpr MemoryAttribute operator~(MemoryAttribute attribute) {
- return static_cast<MemoryAttribute>(~u32(attribute));
-}
-
-constexpr MemoryAttribute& operator|=(MemoryAttribute& lhs, MemoryAttribute rhs) {
- lhs = lhs | rhs;
- return lhs;
-}
-
-constexpr MemoryAttribute& operator&=(MemoryAttribute& lhs, MemoryAttribute rhs) {
- lhs = lhs & rhs;
- return lhs;
-}
-
-constexpr MemoryAttribute& operator^=(MemoryAttribute& lhs, MemoryAttribute rhs) {
- lhs = lhs ^ rhs;
- return lhs;
-}
-
-constexpr u32 ToSvcMemoryAttribute(MemoryAttribute attribute) {
- return static_cast<u32>(attribute & MemoryAttribute::Mask);
-}
-
-// clang-format off
-/// Represents memory states and any relevant flags, as used by the kernel.
-/// svcQueryMemory interprets these by masking away all but the first eight
-/// bits when storing memory state into a MemoryInfo instance.
-enum class MemoryState : u32 {
- Mask = 0xFF,
- FlagProtect = 1U << 8,
- FlagDebug = 1U << 9,
- FlagIPC0 = 1U << 10,
- FlagIPC3 = 1U << 11,
- FlagIPC1 = 1U << 12,
- FlagMapped = 1U << 13,
- FlagCode = 1U << 14,
- FlagAlias = 1U << 15,
- FlagModule = 1U << 16,
- FlagTransfer = 1U << 17,
- FlagQueryPhysicalAddressAllowed = 1U << 18,
- FlagSharedDevice = 1U << 19,
- FlagSharedDeviceAligned = 1U << 20,
- FlagIPCBuffer = 1U << 21,
- FlagMemoryPoolAllocated = 1U << 22,
- FlagMapProcess = 1U << 23,
- FlagUncached = 1U << 24,
- FlagCodeMemory = 1U << 25,
-
- // Wildcard used in range checking to indicate all states.
- All = 0xFFFFFFFF,
-
- // Convenience flag sets to reduce repetition
- IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
-
- CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed |
- FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
-
- DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer |
- FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned |
- FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached,
-
- Unmapped = 0x00,
- Io = 0x01 | FlagMapped,
- Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
- Code = 0x03 | CodeFlags | FlagMapProcess,
- CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory,
- Heap = 0x05 | DataFlags | FlagCodeMemory,
- Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
- ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
- ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
-
- IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
- IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
-
- Stack = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed |
- FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
-
- ThreadLocal = 0x0C | FlagMapped | FlagMemoryPoolAllocated,
-
- TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed |
- FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated |
- FlagUncached,
-
- TransferMemory = 0x0E | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed |
- FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
-
- ProcessMemory = 0x0F | FlagIPC3 | FlagIPC1 | FlagMapped | FlagMemoryPoolAllocated,
-
- // Used to signify an inaccessible or invalid memory region with memory queries
- Inaccessible = 0x10,
-
- IpcBuffer1 = 0x11 | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed |
- FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
-
- IpcBuffer3 = 0x12 | FlagIPC3 | FlagMapped | FlagQueryPhysicalAddressAllowed |
- FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
-
- KernelStack = 0x13 | FlagMapped,
-};
-// clang-format on
-
-constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) {
- return static_cast<MemoryState>(u32(lhs) | u32(rhs));
-}
-
-constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) {
- return static_cast<MemoryState>(u32(lhs) & u32(rhs));
-}
-
-constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) {
- return static_cast<MemoryState>(u32(lhs) ^ u32(rhs));
-}
-
-constexpr MemoryState operator~(MemoryState lhs) {
- return static_cast<MemoryState>(~u32(lhs));
-}
-
-constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) {
- lhs = lhs | rhs;
- return lhs;
-}
-
-constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) {
- lhs = lhs & rhs;
- return lhs;
-}
-
-constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) {
- lhs = lhs ^ rhs;
- return lhs;
-}
-
-constexpr u32 ToSvcMemoryState(MemoryState state) {
- return static_cast<u32>(state & MemoryState::Mask);
-}
-
-struct MemoryInfo {
- u64 base_address;
- u64 size;
- u32 state;
- u32 attributes;
- u32 permission;
- u32 ipc_ref_count;
- u32 device_ref_count;
-};
-static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size.");
-
-struct PageInfo {
- u32 flags;
-};
-
-/**
- * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space
- * with homogeneous attributes across its extents. In this particular implementation each VMA is
- * also backed by a single host memory allocation.
- */
-struct VirtualMemoryArea {
- /// Gets the starting (base) address of this VMA.
- VAddr StartAddress() const {
- return base;
- }
-
- /// Gets the ending address of this VMA.
- VAddr EndAddress() const {
- return base + size - 1;
- }
-
- /// Virtual base address of the region.
- VAddr base = 0;
- /// Size of the region.
- u64 size = 0;
-
- VMAType type = VMAType::Free;
- VMAPermission permissions = VMAPermission::None;
- MemoryState state = MemoryState::Unmapped;
- MemoryAttribute attribute = MemoryAttribute::None;
-
- // Settings for type = AllocatedMemoryBlock
- /// Memory block backing this VMA.
- std::shared_ptr<PhysicalMemory> backing_block = nullptr;
- /// Offset into the backing_memory the mapping starts from.
- std::size_t offset = 0;
-
- // Settings for type = BackingMemory
- /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
- u8* backing_memory = nullptr;
-
- // Settings for type = MMIO
- /// Physical address of the register area this VMA maps to.
- PAddr paddr = 0;
- Common::MemoryHookPointer mmio_handler = nullptr;
-
- /// Tests if this area can be merged to the right with `next`.
- bool CanBeMergedWith(const VirtualMemoryArea& next) const;
-};
-
-/**
- * Manages a process' virtual addressing space. This class maintains a list of allocated and free
- * regions in the address space, along with their attributes, and allows kernel clients to
- * manipulate it, adjusting the page table to match.
- *
- * This is similar in idea and purpose to the VM manager present in operating system kernels, with
- * the main difference being that it doesn't have to support swapping or memory mapping of files.
- * The implementation is also simplified by not having to allocate page frames. See these articles
- * about the Linux kernel for an explantion of the concept and implementation:
- * - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/
- * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
- */
-class VMManager final {
- using VMAMap = std::map<VAddr, VirtualMemoryArea>;
-
-public:
- using VMAHandle = VMAMap::const_iterator;
-
- explicit VMManager(Core::System& system);
- ~VMManager();
-
- /// Clears the address space map, re-initializing with a single free area.
- void Reset(FileSys::ProgramAddressSpaceType type);
-
- /// Finds the VMA in which the given address is included in, or `vma_map.end()`.
- VMAHandle FindVMA(VAddr target) const;
-
- /// Indicates whether or not the given handle is within the VMA map.
- bool IsValidHandle(VMAHandle handle) const;
-
- // TODO(yuriks): Should these functions actually return the handle?
-
- /**
- * Maps part of a ref-counted block of memory at a given address.
- *
- * @param target The guest address to start the mapping at.
- * @param block The block to be mapped.
- * @param offset Offset into `block` to map from.
- * @param size Size of the mapping.
- * @param state MemoryState tag to attach to the VMA.
- */
- ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block,
- std::size_t offset, u64 size, MemoryState state,
- VMAPermission perm = VMAPermission::ReadWrite);
-
- /**
- * Maps an unmanaged host memory pointer at a given address.
- *
- * @param target The guest address to start the mapping at.
- * @param memory The memory to be mapped.
- * @param size Size of the mapping.
- * @param state MemoryState tag to attach to the VMA.
- */
- ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
-
- /**
- * Finds the first free memory region of the given size within
- * the user-addressable ASLR memory region.
- *
- * @param size The size of the desired region in bytes.
- *
- * @returns If successful, the base address of the free region with
- * the given size.
- */
- ResultVal<VAddr> FindFreeRegion(u64 size) const;
-
- /**
- * Finds the first free address range that can hold a region of the desired size
- *
- * @param begin The starting address of the range.
- * This is treated as an inclusive beginning address.
- *
- * @param end The ending address of the range.
- * This is treated as an exclusive ending address.
- *
- * @param size The size of the free region to attempt to locate,
- * in bytes.
- *
- * @returns If successful, the base address of the free region with
- * the given size.
- *
- * @returns If unsuccessful, a result containing an error code.
- *
- * @pre The starting address must be less than the ending address.
- * @pre The size must not exceed the address range itself.
- */
- ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const;
-
- /**
- * Maps a memory-mapped IO region at a given address.
- *
- * @param target The guest address to start the mapping at.
- * @param paddr The physical address where the registers are present.
- * @param size Size of the mapping.
- * @param state MemoryState tag to attach to the VMA.
- * @param mmio_handler The handler that will implement read and write for this MMIO region.
- */
- ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
- Common::MemoryHookPointer mmio_handler);
-
- /// Unmaps a range of addresses, splitting VMAs as necessary.
- ResultCode UnmapRange(VAddr target, u64 size);
-
- /// Changes the permissions of the given VMA.
- VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms);
-
- /// Changes the permissions of a range of addresses, splitting VMAs as necessary.
- ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
-
- ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
-
- /// Attempts to allocate a heap with the given size.
- ///
- /// @param size The size of the heap to allocate in bytes.
- ///
- /// @note If a heap is currently allocated, and this is called
- /// with a size that is equal to the size of the current heap,
- /// then this function will do nothing and return the current
- /// heap's starting address, as there's no need to perform
- /// any additional heap allocation work.
- ///
- /// @note If a heap is currently allocated, and this is called
- /// with a size less than the current heap's size, then
- /// this function will attempt to shrink the heap.
- ///
- /// @note If a heap is currently allocated, and this is called
- /// with a size larger than the current heap's size, then
- /// this function will attempt to extend the size of the heap.
- ///
- /// @returns A result indicating either success or failure.
- /// <p>
- /// If successful, this function will return a result
- /// containing the starting address to the allocated heap.
- /// <p>
- /// If unsuccessful, this function will return a result
- /// containing an error code.
- ///
- /// @pre The given size must lie within the allowable heap
- /// memory region managed by this VMManager instance.
- /// Failure to abide by this will result in ERR_OUT_OF_MEMORY
- /// being returned as the result.
- ///
- ResultVal<VAddr> SetHeapSize(u64 size);
-
- /// Maps memory at a given address.
- ///
- /// @param target The virtual address to map memory at.
- /// @param size The amount of memory to map.
- ///
- /// @note The destination address must lie within the Map region.
- ///
- /// @note This function requires that SystemResourceSize be non-zero,
- /// however, this is just because if it were not then the
- /// resulting page tables could be exploited on hardware by
- /// a malicious program. SystemResource usage does not need
- /// to be explicitly checked or updated here.
- ResultCode MapPhysicalMemory(VAddr target, u64 size);
-
- /// Unmaps memory at a given address.
- ///
- /// @param target The virtual address to unmap memory at.
- /// @param size The amount of memory to unmap.
- ///
- /// @note The destination address must lie within the Map region.
- ///
- /// @note This function requires that SystemResourceSize be non-zero,
- /// however, this is just because if it were not then the
- /// resulting page tables could be exploited on hardware by
- /// a malicious program. SystemResource usage does not need
- /// to be explicitly checked or updated here.
- ResultCode UnmapPhysicalMemory(VAddr target, u64 size);
-
- /// Maps a region of memory as code memory.
- ///
- /// @param dst_address The base address of the region to create the aliasing memory region.
- /// @param src_address The base address of the region to be aliased.
- /// @param size The total amount of memory to map in bytes.
- ///
- /// @pre Both memory regions lie within the actual addressable address space.
- ///
- /// @post After this function finishes execution, assuming success, then the address range
- /// [dst_address, dst_address+size) will alias the memory region,
- /// [src_address, src_address+size).
- /// <p>
- /// What this also entails is as follows:
- /// 1. The aliased region gains the Locked memory attribute.
- /// 2. The aliased region becomes read-only.
- /// 3. The aliasing region becomes read-only.
- /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule.
- ///
- ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
-
- /// Unmaps a region of memory designated as code module memory.
- ///
- /// @param dst_address The base address of the memory region aliasing the source memory region.
- /// @param src_address The base address of the memory region being aliased.
- /// @param size The size of the memory region to unmap in bytes.
- ///
- /// @pre Both memory ranges lie within the actual addressable address space.
- ///
- /// @pre The memory region being unmapped has been previously been mapped
- /// by a call to MapCodeMemory.
- ///
- /// @post After execution of the function, if successful. the aliasing memory region
- /// will be unmapped and the aliased region will have various traits about it
- /// restored to what they were prior to the original mapping call preceding
- /// this function call.
- /// <p>
- /// What this also entails is as follows:
- /// 1. The state of the memory region will now indicate a general heap region.
- /// 2. All memory attributes for the memory region are cleared.
- /// 3. Memory permissions for the region are restored to user read/write.
- ///
- ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
-
- /// Queries the memory manager for information about the given address.
- ///
- /// @param address The address to query the memory manager about for information.
- ///
- /// @return A MemoryInfo instance containing information about the given address.
- ///
- MemoryInfo QueryMemory(VAddr address) const;
-
- /// Sets an attribute across the given address range.
- ///
- /// @param address The starting address
- /// @param size The size of the range to set the attribute on.
- /// @param mask The attribute mask
- /// @param attribute The attribute to set across the given address range
- ///
- /// @returns RESULT_SUCCESS if successful
- /// @returns ERR_INVALID_ADDRESS_STATE if the attribute could not be set.
- ///
- ResultCode SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
- MemoryAttribute attribute);
-
- /**
- * Scans all VMAs and updates the page table range of any that use the given vector as backing
- * memory. This should be called after any operation that causes reallocation of the vector.
- */
- void RefreshMemoryBlockMappings(const PhysicalMemory* block);
-
- /// Dumps the address space layout to the log, for debugging
- void LogLayout() const;
-
- /// Gets the total memory usage, used by svcGetInfo
- u64 GetTotalPhysicalMemoryAvailable() const;
-
- /// Gets the address space base address
- VAddr GetAddressSpaceBaseAddress() const;
-
- /// Gets the address space end address
- VAddr GetAddressSpaceEndAddress() const;
-
- /// Gets the total address space address size in bytes
- u64 GetAddressSpaceSize() const;
-
- /// Gets the address space width in bits.
- u64 GetAddressSpaceWidth() const;
-
- /// Determines whether or not the given address range lies within the address space.
- bool IsWithinAddressSpace(VAddr address, u64 size) const;
-
- /// Gets the base address of the ASLR region.
- VAddr GetASLRRegionBaseAddress() const;
-
- /// Gets the end address of the ASLR region.
- VAddr GetASLRRegionEndAddress() const;
-
- /// Gets the size of the ASLR region
- u64 GetASLRRegionSize() const;
-
- /// Determines whether or not the specified address range is within the ASLR region.
- bool IsWithinASLRRegion(VAddr address, u64 size) const;
-
- /// Gets the base address of the code region.
- VAddr GetCodeRegionBaseAddress() const;
-
- /// Gets the end address of the code region.
- VAddr GetCodeRegionEndAddress() const;
-
- /// Gets the total size of the code region in bytes.
- u64 GetCodeRegionSize() const;
-
- /// Determines whether or not the specified range is within the code region.
- bool IsWithinCodeRegion(VAddr address, u64 size) const;
-
- /// Gets the base address of the heap region.
- VAddr GetHeapRegionBaseAddress() const;
-
- /// Gets the end address of the heap region;
- VAddr GetHeapRegionEndAddress() const;
-
- /// Gets the total size of the heap region in bytes.
- u64 GetHeapRegionSize() const;
-
- /// Gets the total size of the current heap in bytes.
- ///
- /// @note This is the current allocated heap size, not the size
- /// of the region it's allowed to exist within.
- ///
- u64 GetCurrentHeapSize() const;
-
- /// Determines whether or not the specified range is within the heap region.
- bool IsWithinHeapRegion(VAddr address, u64 size) const;
-
- /// Gets the base address of the map region.
- VAddr GetMapRegionBaseAddress() const;
-
- /// Gets the end address of the map region.
- VAddr GetMapRegionEndAddress() const;
-
- /// Gets the total size of the map region in bytes.
- u64 GetMapRegionSize() const;
-
- /// Determines whether or not the specified range is within the map region.
- bool IsWithinMapRegion(VAddr address, u64 size) const;
-
- /// Gets the base address of the stack region.
- VAddr GetStackRegionBaseAddress() const;
-
- /// Gets the end address of the stack region.
- VAddr GetStackRegionEndAddress() const;
-
- /// Gets the total size of the stack region in bytes.
- u64 GetStackRegionSize() const;
-
- /// Determines whether or not the given address range is within the stack region
- bool IsWithinStackRegion(VAddr address, u64 size) const;
-
- /// Gets the base address of the TLS IO region.
- VAddr GetTLSIORegionBaseAddress() const;
-
- /// Gets the end address of the TLS IO region.
- VAddr GetTLSIORegionEndAddress() const;
-
- /// Gets the total size of the TLS IO region in bytes.
- u64 GetTLSIORegionSize() const;
-
- /// Determines if the given address range is within the TLS IO region.
- bool IsWithinTLSIORegion(VAddr address, u64 size) const;
-
- /// Each VMManager has its own page table, which is set as the main one when the owning process
- /// is scheduled.
- Common::PageTable page_table{Memory::PAGE_BITS};
-
- using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
-
- /// Checks if an address range adheres to the specified states provided.
- ///
- /// @param address The starting address of the address range.
- /// @param size The size of the address range.
- /// @param state_mask The memory state mask.
- /// @param state The state to compare the individual VMA states against,
- /// which is done in the form of: (vma.state & state_mask) != state.
- /// @param permission_mask The memory permissions mask.
- /// @param permissions The permission to compare the individual VMA permissions against,
- /// which is done in the form of:
- /// (vma.permission & permission_mask) != permission.
- /// @param attribute_mask The memory attribute mask.
- /// @param attribute The memory attributes to compare the individual VMA attributes
- /// against, which is done in the form of:
- /// (vma.attributes & attribute_mask) != attribute.
- /// @param ignore_mask The memory attributes to ignore during the check.
- ///
- /// @returns If successful, returns a tuple containing the memory attributes
- /// (with ignored bits specified by ignore_mask unset), memory permissions, and
- /// memory state across the memory range.
- /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
- ///
- CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
- VMAPermission permission_mask, VMAPermission permissions,
- MemoryAttribute attribute_mask, MemoryAttribute attribute,
- MemoryAttribute ignore_mask) const;
-
-private:
- using VMAIter = VMAMap::iterator;
-
- /// Converts a VMAHandle to a mutable VMAIter.
- VMAIter StripIterConstness(const VMAHandle& iter);
-
- /// Unmaps the given VMA.
- VMAIter Unmap(VMAIter vma);
-
- /**
- * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing
- * the appropriate error checking.
- */
- ResultVal<VMAIter> CarveVMA(VAddr base, u64 size);
-
- /**
- * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each
- * end of the range.
- */
- ResultVal<VMAIter> CarveVMARange(VAddr base, u64 size);
-
- /**
- * Splits a VMA in two, at the specified offset.
- * @returns the right side of the split, with the original iterator becoming the left side.
- */
- VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma);
-
- /**
- * Checks for and merges the specified VMA with adjacent ones if possible.
- * @returns the merged VMA or the original if no merging was possible.
- */
- VMAIter MergeAdjacent(VMAIter vma);
-
- /**
- * Merges two adjacent VMAs.
- */
- void MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right);
-
- /// Updates the pages corresponding to this VMA so they match the VMA's attributes.
- void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
-
- /// Initializes memory region ranges to adhere to a given address space type.
- void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type);
-
- /// Clears the underlying map and page table.
- void Clear();
-
- /// Clears out the VMA map, unmapping any previously mapped ranges.
- void ClearVMAMap();
-
- /// Clears out the page table
- void ClearPageTable();
-
- /// Gets the amount of memory currently mapped (state != Unmapped) in a range.
- ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;
-
- /// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range.
- ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
- std::size_t size) const;
-
- /**
- * A map covering the entirety of the managed address space, keyed by the `base` field of each
- * VMA. It must always be modified by splitting or merging VMAs, so that the invariant
- * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be
- * merged when possible so that no two similar and adjacent regions exist that have not been
- * merged.
- */
- VMAMap vma_map;
-
- u32 address_space_width = 0;
- 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;
-
- VAddr heap_region_base = 0;
- VAddr heap_region_end = 0;
-
- VAddr map_region_base = 0;
- VAddr map_region_end = 0;
-
- VAddr stack_region_base = 0;
- VAddr stack_region_end = 0;
-
- VAddr tls_io_region_base = 0;
- VAddr tls_io_region_end = 0;
-
- // Memory used to back the allocations in the regular heap. A single vector is used to cover
- // the entire virtual address space extents that bound the allocations, including any holes.
- // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
- // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
- std::shared_ptr<PhysicalMemory> heap_memory;
-
- // The end of the currently allocated heap. This is not an inclusive
- // end of the range. This is essentially 'base_address + current_size'.
- VAddr heap_end = 0;
-
- // The current amount of memory mapped via MapPhysicalMemory.
- // This is used here (and in Nintendo's kernel) only for debugging, and does not impact
- // any behavior.
- u64 physical_memory_mapped = 0;
-
- Core::System& system;
-};
-} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 450f61fea..b6bdbd988 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -342,8 +342,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
*/
#define CASCADE_RESULT(target, source) \
auto CONCAT2(check_result_L, __LINE__) = source; \
- if (CONCAT2(check_result_L, __LINE__).Failed()) \
+ if (CONCAT2(check_result_L, __LINE__).Failed()) { \
return CONCAT2(check_result_L, __LINE__).Code(); \
+ } \
target = std::move(*CONCAT2(check_result_L, __LINE__))
/**
@@ -351,6 +352,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
* non-success, or discarded otherwise.
*/
#define CASCADE_CODE(source) \
- auto CONCAT2(check_result_L, __LINE__) = source; \
- if (CONCAT2(check_result_L, __LINE__).IsError()) \
- return CONCAT2(check_result_L, __LINE__);
+ do { \
+ auto CONCAT2(check_result_L, __LINE__) = source; \
+ if (CONCAT2(check_result_L, __LINE__).IsError()) { \
+ return CONCAT2(check_result_L, __LINE__); \
+ } \
+ } while (false)
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index cfac8ca9a..c2c11dbcb 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -11,6 +11,7 @@
#include "common/string_util.h"
#include "common/swap.h"
#include "core/constants.h"
+#include "core/core.h"
#include "core/core_timing.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -35,7 +36,7 @@ constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
static std::string GetImagePath(Common::UUID uuid) {
- return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
+ return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
}
@@ -44,6 +45,218 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
return static_cast<u32>(std::min(size, max_jpeg_image_size));
}
+class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> {
+public:
+ explicit IManagerForSystemService(Common::UUID user_id)
+ : ServiceFramework("IManagerForSystemService") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CheckAvailability"},
+ {1, nullptr, "GetAccountId"},
+ {2, nullptr, "EnsureIdTokenCacheAsync"},
+ {3, nullptr, "LoadIdTokenCache"},
+ {100, nullptr, "SetSystemProgramIdentification"},
+ {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
+ {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
+ {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
+ {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
+ {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
+ {120, nullptr, "GetNintendoAccountId"},
+ {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
+ {130, nullptr, "GetNintendoAccountUserResourceCache"},
+ {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
+ {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
+ {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
+ {134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+
+ {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
+ {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
+ {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
+ {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
+ {150, nullptr, "CreateAuthorizationRequest"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+// 3.0.0+
+class IFloatingRegistrationRequest final : public ServiceFramework<IFloatingRegistrationRequest> {
+public:
+ explicit IFloatingRegistrationRequest(Common::UUID user_id)
+ : ServiceFramework("IFloatingRegistrationRequest") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetSessionId"},
+ {12, nullptr, "GetAccountId"},
+ {13, nullptr, "GetLinkedNintendoAccountId"},
+ {14, nullptr, "GetNickname"},
+ {15, nullptr, "GetProfileImage"},
+ {21, nullptr, "LoadIdTokenCache"},
+ {100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync
+ {101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync
+ {102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+
+ {103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+
+ {110, nullptr, "SetSystemProgramIdentification"},
+ {111, nullptr, "EnsureIdTokenCacheAsync"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IAdministrator final : public ServiceFramework<IAdministrator> {
+public:
+ explicit IAdministrator(Common::UUID user_id) : ServiceFramework("IAdministrator") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CheckAvailability"},
+ {1, nullptr, "GetAccountId"},
+ {2, nullptr, "EnsureIdTokenCacheAsync"},
+ {3, nullptr, "LoadIdTokenCache"},
+ {100, nullptr, "SetSystemProgramIdentification"},
+ {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
+ {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
+ {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
+ {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
+ {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
+ {120, nullptr, "GetNintendoAccountId"},
+ {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
+ {130, nullptr, "GetNintendoAccountUserResourceCache"},
+ {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
+ {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
+ {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
+ {134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+
+ {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
+ {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
+ {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
+ {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
+ {150, nullptr, "CreateAuthorizationRequest"},
+ {200, nullptr, "IsRegistered"},
+ {201, nullptr, "RegisterAsync"},
+ {202, nullptr, "UnregisterAsync"},
+ {203, nullptr, "DeleteRegistrationInfoLocally"},
+ {220, nullptr, "SynchronizeProfileAsync"},
+ {221, nullptr, "UploadProfileAsync"},
+ {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"},
+ {250, nullptr, "IsLinkedWithNintendoAccount"},
+ {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"},
+ {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"},
+ {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"},
+ {256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"},
+ {260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
+ {261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
+ {280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"},
+ {290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+
+ {300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+
+ {400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+
+ {401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+
+ {900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+
+ {901, nullptr, "ImportAsyncForWin"}, // 9.0.0+
+ {997, nullptr, "DebugUnlinkNintendoAccountAsync"},
+ {998, nullptr, "DebugSetAvailabilityErrorDetail"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> {
+public:
+ explicit IAuthorizationRequest(Common::UUID user_id)
+ : ServiceFramework("IAuthorizationRequest") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetSessionId"},
+ {10, nullptr, "InvokeWithoutInteractionAsync"},
+ {19, nullptr, "IsAuthorized"},
+ {20, nullptr, "GetAuthorizationCode"},
+ {21, nullptr, "GetIdToken"},
+ {22, nullptr, "GetState"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IOAuthProcedure final : public ServiceFramework<IOAuthProcedure> {
+public:
+ explicit IOAuthProcedure(Common::UUID user_id) : ServiceFramework("IOAuthProcedure") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "PrepareAsync"},
+ {1, nullptr, "GetRequest"},
+ {2, nullptr, "ApplyResponse"},
+ {3, nullptr, "ApplyResponseAsync"},
+ {10, nullptr, "Suspend"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+// 3.0.0+
+class IOAuthProcedureForExternalNsa final : public ServiceFramework<IOAuthProcedureForExternalNsa> {
+public:
+ explicit IOAuthProcedureForExternalNsa(Common::UUID user_id)
+ : ServiceFramework("IOAuthProcedureForExternalNsa") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "PrepareAsync"},
+ {1, nullptr, "GetRequest"},
+ {2, nullptr, "ApplyResponse"},
+ {3, nullptr, "ApplyResponseAsync"},
+ {10, nullptr, "Suspend"},
+ {100, nullptr, "GetAccountId"},
+ {101, nullptr, "GetLinkedNintendoAccountId"},
+ {102, nullptr, "GetNickname"},
+ {103, nullptr, "GetProfileImage"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IOAuthProcedureForNintendoAccountLinkage final
+ : public ServiceFramework<IOAuthProcedureForNintendoAccountLinkage> {
+public:
+ explicit IOAuthProcedureForNintendoAccountLinkage(Common::UUID user_id)
+ : ServiceFramework("IOAuthProcedureForNintendoAccountLinkage") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "PrepareAsync"},
+ {1, nullptr, "GetRequest"},
+ {2, nullptr, "ApplyResponse"},
+ {3, nullptr, "ApplyResponseAsync"},
+ {10, nullptr, "Suspend"},
+ {100, nullptr, "GetRequestWithTheme"},
+ {101, nullptr, "IsNetworkServiceAccountReplaced"},
+ {199, nullptr, "GetUrlForIntroductionOfExtraMembership"}, // 2.0.0 - 5.1.0
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class INotifier final : public ServiceFramework<INotifier> {
+public:
+ explicit INotifier(Common::UUID user_id) : ServiceFramework("INotifier") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetSystemEvent"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
class IProfileCommon : public ServiceFramework<IProfileCommon> {
public:
explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id,
@@ -74,9 +287,7 @@ protected:
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
- std::array<u8, sizeof(ProfileData)> raw_data;
- std::memcpy(raw_data.data(), &data, sizeof(ProfileData));
- ctx.WriteBuffer(raw_data);
+ ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
@@ -108,7 +319,7 @@ protected:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+ const Common::FS::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...");
@@ -121,7 +332,7 @@ protected:
std::vector<u8> buffer(size);
image.ReadBytes(buffer.data(), buffer.size());
- ctx.WriteBuffer(buffer.data(), buffer.size());
+ ctx.WriteBuffer(buffer);
rb.Push<u32>(size);
}
@@ -130,7 +341,7 @@ protected:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- const FileUtil::IOFile image(GetImagePath(user_id), "rb");
+ const Common::FS::IOFile image(GetImagePath(user_id), "rb");
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
@@ -195,7 +406,7 @@ protected:
ProfileData data;
std::memcpy(&data, user_data.data(), sizeof(ProfileData));
- FileUtil::IOFile image(GetImagePath(user_id), "wb");
+ Common::FS::IOFile image(GetImagePath(user_id), "wb");
if (!image.IsOpen() || !image.Resize(image_data.size()) ||
image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
@@ -226,9 +437,58 @@ public:
: IProfileCommon("IProfileEditor", true, user_id, profile_manager) {}
};
+class IAsyncContext final : public ServiceFramework<IAsyncContext> {
+public:
+ explicit IAsyncContext(Common::UUID user_id) : ServiceFramework("IAsyncContext") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetSystemEvent"},
+ {1, nullptr, "Cancel"},
+ {2, nullptr, "HasDone"},
+ {3, nullptr, "GetResult"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class ISessionObject final : public ServiceFramework<ISessionObject> {
+public:
+ explicit ISessionObject(Common::UUID user_id) : ServiceFramework("ISessionObject") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {999, nullptr, "Dummy"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IGuestLoginRequest final : public ServiceFramework<IGuestLoginRequest> {
+public:
+ explicit IGuestLoginRequest(Common::UUID) : ServiceFramework("IGuestLoginRequest") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetSessionId"},
+ {11, nullptr, "Unknown"}, // 1.0.0 - 2.3.0 (the name is blank on Switchbrew)
+ {12, nullptr, "GetAccountId"},
+ {13, nullptr, "GetLinkedNintendoAccountId"},
+ {14, nullptr, "GetNickname"},
+ {15, nullptr, "GetProfileImage"},
+ {21, nullptr, "LoadIdTokenCache"}, // 3.0.0+
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
public:
- IManagerForApplication() : ServiceFramework("IManagerForApplication") {
+ explicit IManagerForApplication(Common::UUID user_id)
+ : ServiceFramework("IManagerForApplication"), user_id(user_id) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
@@ -237,7 +497,7 @@ public:
{3, nullptr, "LoadIdTokenCache"},
{130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
{150, nullptr, "CreateAuthorizationRequest"},
- {160, nullptr, "StoreOpenContext"},
+ {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
{170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
};
// clang-format on
@@ -254,11 +514,100 @@ private:
}
void GetAccountId(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_ACC, "(STUBBED) called");
- // Should return a nintendo account ID
+ LOG_DEBUG(Service_ACC, "called");
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u64>(1);
+ rb.PushRaw<u64>(user_id.GetNintendoID());
+ }
+
+ void StoreOpenContext(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ Common::UUID user_id;
+};
+
+// 6.0.0+
+class IAsyncNetworkServiceLicenseKindContext final
+ : public ServiceFramework<IAsyncNetworkServiceLicenseKindContext> {
+public:
+ explicit IAsyncNetworkServiceLicenseKindContext(Common::UUID user_id)
+ : ServiceFramework("IAsyncNetworkServiceLicenseKindContext") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetSystemEvent"},
+ {1, nullptr, "Cancel"},
+ {2, nullptr, "HasDone"},
+ {3, nullptr, "GetResult"},
+ {4, nullptr, "GetNetworkServiceLicenseKind"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+// 8.0.0+
+class IOAuthProcedureForUserRegistration final
+ : public ServiceFramework<IOAuthProcedureForUserRegistration> {
+public:
+ explicit IOAuthProcedureForUserRegistration(Common::UUID user_id)
+ : ServiceFramework("IOAuthProcedureForUserRegistration") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "PrepareAsync"},
+ {1, nullptr, "GetRequest"},
+ {2, nullptr, "ApplyResponse"},
+ {3, nullptr, "ApplyResponseAsync"},
+ {10, nullptr, "Suspend"},
+ {100, nullptr, "GetAccountId"},
+ {101, nullptr, "GetLinkedNintendoAccountId"},
+ {102, nullptr, "GetNickname"},
+ {103, nullptr, "GetProfileImage"},
+ {110, nullptr, "RegisterUserAsync"},
+ {111, nullptr, "GetUid"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class DAUTH_O final : public ServiceFramework<DAUTH_O> {
+public:
+ explicit DAUTH_O(Common::UUID) : ServiceFramework("dauth:o") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, // [5.0.0-5.1.0] GeneratePostData
+ {1, nullptr, "LoadAuthenticationTokenCache"}, // 6.0.0+
+ {2, nullptr, "InvalidateAuthenticationTokenCache"}, // 6.0.0+
+ {10, nullptr, "EnsureEdgeTokenCacheAsync"}, // 6.0.0+
+ {11, nullptr, "LoadEdgeTokenCache"}, // 6.0.0+
+ {12, nullptr, "InvalidateEdgeTokenCache"}, // 6.0.0+
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+// 6.0.0+
+class IAsyncResult final : public ServiceFramework<IAsyncResult> {
+public:
+ explicit IAsyncResult(Common::UUID user_id) : ServiceFramework("IAsyncResult") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetResult"},
+ {1, nullptr, "Cancel"},
+ {2, nullptr, "IsAvailable"},
+ {3, nullptr, "GetSystemEvent"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
}
};
@@ -319,46 +668,37 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon
void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto pid = rp.Pop<u64>();
- LOG_DEBUG(Service_ACC, "called, process_id={}", pid);
+ LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(InitializeApplicationInfoBase(pid));
+ rb.Push(InitializeApplicationInfoBase());
}
void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto pid = rp.Pop<u64>();
- LOG_WARNING(Service_ACC, "(Partial implementation) called, process_id={}", pid);
+ LOG_WARNING(Service_ACC, "(Partial implementation) called");
// TODO(ogniK): We require checking if the user actually owns the title and what not. As of
// currently, we assume the user owns the title. InitializeApplicationInfoBase SHOULD be called
// first then we do extra checks if the game is a digital copy.
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(InitializeApplicationInfoBase(pid));
+ rb.Push(InitializeApplicationInfoBase());
}
-ResultCode Module::Interface::InitializeApplicationInfoBase(u64 process_id) {
+ResultCode Module::Interface::InitializeApplicationInfoBase() {
if (application_info) {
LOG_ERROR(Service_ACC, "Application already initialized");
return ERR_ACCOUNTINFO_ALREADY_INITIALIZED;
}
- const auto& list = system.Kernel().GetProcessList();
- const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
- return process->GetProcessID() == process_id;
- });
-
- if (iter == list.end()) {
- LOG_ERROR(Service_ACC, "Failed to find process ID");
- application_info.application_type = ApplicationType::Unknown;
-
- return ERR_ACCOUNTINFO_BAD_APPLICATION;
- }
-
- const auto launch_property = system.GetARPManager().GetLaunchProperty((*iter)->GetTitleID());
+ // TODO(ogniK): This should be changed to reflect the target process for when we have multiple
+ // processes emulated. As we don't actually have pid support we should assume we're just using
+ // our own process
+ const auto& current_process = system.Kernel().CurrentProcess();
+ const auto launch_property =
+ system.GetARPManager().GetLaunchProperty(current_process->GetTitleID());
if (launch_property.Failed()) {
LOG_ERROR(Service_ACC, "Failed to get launch property");
@@ -372,10 +712,12 @@ ResultCode Module::Interface::InitializeApplicationInfoBase(u64 process_id) {
case FileSys::StorageId::Host:
case FileSys::StorageId::NandUser:
case FileSys::StorageId::SdCard:
+ case FileSys::StorageId::None: // Yuzu specific, differs from hardware
application_info.application_type = ApplicationType::Digital;
break;
default:
- LOG_ERROR(Service_ACC, "Invalid game storage ID");
+ LOG_ERROR(Service_ACC, "Invalid game storage ID! storage_id={}",
+ launch_property->base_game_storage_id);
return ERR_ACCOUNTINFO_BAD_APPLICATION;
}
@@ -389,7 +731,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IManagerForApplication>();
+ rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser());
}
void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
@@ -400,8 +742,10 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
bool is_locked = false;
if (res != Loader::ResultStatus::Success) {
- FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
- auto nacp_unique = pm.GetControlMetadata().first;
+ const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ const auto nacp_unique = pm.GetControlMetadata().first;
if (nacp_unique != nullptr) {
is_locked = nacp_unique->GetUserAccountSwitchLock();
@@ -428,6 +772,37 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IProfileEditor>(user_id, *profile_manager);
}
+void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ACC, "called");
+
+ // All users should be qualified. We don't actually have parental control or anything to do with
+ // nintendo online currently. We're just going to assume the user running the game has access to
+ // the game regardless of parental control settings.
+ ctx.WriteBuffer(profile_manager->GetAllUsers());
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
+
+ // This is similar to GetBaasAccountManagerForApplication
+ // This command is used concurrently with ListOpenContextStoredUsers
+ // TODO: Find the differences between this and GetBaasAccountManagerForApplication
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser());
+}
+
+void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
+
+ // TODO(ogniK): Handle open contexts
+ ctx.WriteBuffer(profile_manager->GetOpenUsers());
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called");
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 7a7dc9ec6..c611efd89 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -33,9 +33,12 @@ public:
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
void GetProfileEditor(Kernel::HLERequestContext& ctx);
+ void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
+ void LoadOpenContext(Kernel::HLERequestContext& ctx);
+ void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
private:
- ResultCode InitializeApplicationInfoBase(u64 process_id);
+ ResultCode InitializeApplicationInfoBase();
enum class ApplicationType : u32_le {
GameCard = 0,
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp
index 3bac6bcd1..51f119b12 100644
--- a/src/core/hle/service/acc/acc_aa.cpp
+++ b/src/core/hle/service/acc/acc_aa.cpp
@@ -13,8 +13,8 @@ ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{0, nullptr, "EnsureCacheAsync"},
{1, nullptr, "LoadCache"},
{2, nullptr, "GetDeviceAccountId"},
- {50, nullptr, "RegisterNotificationTokenAsync"},
- {51, nullptr, "UnregisterNotificationTokenAsync"},
+ {50, nullptr, "RegisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0
+ {51, nullptr, "UnregisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index b941c260b..d2bb8c2c8 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -17,26 +17,28 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{3, &ACC_SU::ListOpenUsers, "ListOpenUsers"},
{4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_SU::GetProfile, "GetProfile"},
- {6, nullptr, "GetProfileDigest"},
+ {6, nullptr, "GetProfileDigest"}, // 3.0.0+
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
- {60, nullptr, "ListOpenContextStoredUsers"},
- {99, nullptr, "DebugActivateOpenContextRetention"},
+ {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
+ {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},
{102, nullptr, "GetBaasAccountManagerForSystemService"},
{103, nullptr, "GetBaasUserAvailabilityChangeNotifier"},
{104, nullptr, "GetProfileUpdateNotifier"},
- {105, nullptr, "CheckNetworkServiceAvailabilityAsync"},
- {106, nullptr, "GetProfileSyncNotifier"},
+ {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
+ {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
{110, nullptr, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{112, nullptr, "LoadSaveDataThumbnail"},
- {113, nullptr, "GetSaveDataThumbnailExistence"},
- {130, nullptr, "ActivateOpenContextRetention"},
- {140, nullptr, "ListQualifiedUsers"},
- {190, nullptr, "GetUserLastOpenedApplication"},
- {191, nullptr, "ActivateOpenContextHolder"},
+ {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
+ {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+
+ {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+
+ {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
+ {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+
+ {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0
+ {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+
{200, nullptr, "BeginUserRegistration"},
{201, nullptr, "CompleteUserRegistration"},
{202, nullptr, "CancelUserRegistration"},
@@ -44,15 +46,15 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{204, nullptr, "SetUserPosition"},
{205, &ACC_SU::GetProfileEditor, "GetProfileEditor"},
{206, nullptr, "CompleteUserRegistrationForcibly"},
- {210, nullptr, "CreateFloatingRegistrationRequest"},
- {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"},
- {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"},
+ {210, nullptr, "CreateFloatingRegistrationRequest"}, // 3.0.0+
+ {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+
+ {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+
{230, nullptr, "AuthenticateServiceAsync"},
{250, nullptr, "GetBaasAccountAdministrator"},
{290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"},
- {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"},
+ {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, // 3.0.0+
{299, nullptr, "SuspendBackgroundDaemon"},
- {997, nullptr, "DebugInvalidateTokenCacheForUser"},
+ {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+
{998, nullptr, "DebugSetUserStateClose"},
{999, nullptr, "DebugSetUserStateOpen"},
};
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 0ac19f4ff..75a24f8f5 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -17,23 +17,23 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
{4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U0::GetProfile, "GetProfile"},
- {6, nullptr, "GetProfileDigest"},
+ {6, nullptr, "GetProfileDigest"}, // 3.0.0+
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
- {60, nullptr, "ListOpenContextStoredUsers"},
- {99, nullptr, "DebugActivateOpenContextRetention"},
+ {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
+ {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
{102, nullptr, "AuthenticateApplicationAsync"},
- {103, nullptr, "CheckNetworkServiceAvailabilityAsync"},
+ {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
{110, nullptr, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{120, nullptr, "CreateGuestLoginRequest"},
- {130, nullptr, "LoadOpenContext"},
- {131, nullptr, "ListOpenContextStoredUsers"},
- {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"},
- {141, nullptr, "ListQualifiedUsers"},
- {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
+ {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+
+ {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
+ {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
+ {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
+ {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+
};
// clang-format on
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 858e91dde..a4aa5316a 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -17,27 +17,29 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{3, &ACC_U1::ListOpenUsers, "ListOpenUsers"},
{4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U1::GetProfile, "GetProfile"},
- {6, nullptr, "GetProfileDigest"},
+ {6, nullptr, "GetProfileDigest"}, // 3.0.0+
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
- {60, nullptr, "ListOpenContextStoredUsers"},
- {99, nullptr, "DebugActivateOpenContextRetention"},
+ {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
+ {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},
{102, nullptr, "GetBaasAccountManagerForSystemService"},
- {103, nullptr, "GetProfileUpdateNotifier"},
- {104, nullptr, "CheckNetworkServiceAvailabilityAsync"},
- {105, nullptr, "GetBaasUserAvailabilityChangeNotifier"},
- {106, nullptr, "GetProfileSyncNotifier"},
+ {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"},
+ {104, nullptr, "GetProfileUpdateNotifier"},
+ {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
+ {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
{110, nullptr, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{112, nullptr, "LoadSaveDataThumbnail"},
- {113, nullptr, "GetSaveDataThumbnailExistence"},
- {130, nullptr, "ActivateOpenContextRetention"},
- {140, nullptr, "ListQualifiedUsers"},
- {190, nullptr, "GetUserLastOpenedApplication"},
- {191, nullptr, "ActivateOpenContextHolder"},
- {997, nullptr, "DebugInvalidateTokenCacheForUser"},
+ {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
+ {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+
+ {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+
+ {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
+ {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+
+ {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0
+ {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+
+ {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+
{998, nullptr, "DebugSetUserStateClose"},
{999, nullptr, "DebugSetUserStateOpen"},
};
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index eb8c81645..9b829e957 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -13,6 +13,7 @@
namespace Service::Account {
+namespace FS = Common::FS;
using Common::UUID;
struct UserRaw {
@@ -58,7 +59,7 @@ ProfileManager::~ProfileManager() {
/// internal management of the users profiles
std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
if (user_count >= MAX_USERS) {
- return {};
+ return std::nullopt;
}
profiles[user_count] = profile;
return user_count++;
@@ -101,13 +102,14 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
[&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) {
return ERROR_USER_ALREADY_EXISTS;
}
- ProfileInfo profile;
- profile.user_uuid = uuid;
- profile.username = username;
- profile.data = {};
- profile.creation_time = 0x0;
- profile.is_open = false;
- return AddUser(profile);
+
+ return AddUser({
+ .user_uuid = uuid,
+ .username = username,
+ .creation_time = 0,
+ .data = {},
+ .is_open = false,
+ });
}
/// Creates a new user on the system. This function allows a much simpler method of registration
@@ -126,7 +128,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
if (index >= MAX_USERS) {
- return {};
+ return std::nullopt;
}
return profiles[index].user_uuid;
@@ -135,13 +137,13 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
/// Returns a users profile index based on their user id.
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
- return {};
+ return std::nullopt;
}
const auto iter = std::find_if(profiles.begin(), profiles.end(),
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
if (iter == profiles.end()) {
- return {};
+ return std::nullopt;
}
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
@@ -317,9 +319,8 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
}
void ProfileManager::ParseUserSaveFile() {
- FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
- ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
- "rb");
+ const FS::IOFile save(
+ FS::GetUserPath(FS::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 "
@@ -339,7 +340,13 @@ void ProfileManager::ParseUserSaveFile() {
continue;
}
- AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false});
+ AddUser({
+ .user_uuid = user.uuid,
+ .username = user.username,
+ .creation_time = user.timestamp,
+ .data = user.extra_data,
+ .is_open = false,
+ });
}
std::stable_partition(profiles.begin(), profiles.end(),
@@ -350,29 +357,31 @@ 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;
- raw.users[i].extra_data = profiles[i].data;
+ raw.users[i] = {
+ .uuid = profiles[i].user_uuid,
+ .uuid2 = profiles[i].user_uuid,
+ .timestamp = profiles[i].creation_time,
+ .username = profiles[i].username,
+ .extra_data = profiles[i].data,
+ };
}
- 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 raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010";
+ if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) {
+ FS::Delete(raw_path);
+ }
- const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
- ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
+ const auto path =
+ FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
- if (!FileUtil::CreateFullPath(path)) {
+ if (!FS::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");
+ FS::IOFile save(path, "wb");
if (!save.IsOpen()) {
LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 557608e76..63421b963 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
@@ -43,20 +44,15 @@
namespace Service::AM {
-constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
-constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
-constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
+constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2};
+constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 3};
+constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503};
enum class LaunchParameterKind : u32 {
ApplicationSpecific = 1,
AccountPreselectedUser = 2,
};
-enum class VrMode : u8 {
- Disabled = 0,
- Enabled = 1,
-};
-
constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
struct LaunchParameterAccountPreselectedUser {
@@ -73,6 +69,7 @@ IWindowController::IWindowController(Core::System& system_)
static const FunctionInfo functions[] = {
{0, nullptr, "CreateWindow"},
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
+ {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"},
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
{11, nullptr, "ReleaseForegroundRights"},
{12, nullptr, "RejectToChangeIntoBackground"},
@@ -194,8 +191,8 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
{5, nullptr, "GetLastForegroundCaptureImageEx"},
{6, nullptr, "GetLastApplicationCaptureImageEx"},
{7, nullptr, "GetCallerAppletCaptureImageEx"},
- {8, nullptr, "TakeScreenShotOfOwnLayer"}, // 2.0.0+
- {9, nullptr, "CopyBetweenCaptureBuffers"}, // 5.0.0+
+ {8, nullptr, "TakeScreenShotOfOwnLayer"},
+ {9, nullptr, "CopyBetweenCaptureBuffers"},
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
{11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
{12, nullptr, "AcquireLastForegroundCaptureBuffer"},
@@ -205,17 +202,14 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
{16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
{17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
{18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
- // 2.0.0+
{20, nullptr, "ClearCaptureBuffer"},
{21, nullptr, "ClearAppletTransitionBuffer"},
- // 4.0.0+
{22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
- // 6.0.0+
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
};
// clang-format on
@@ -230,11 +224,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
static const FunctionInfo functions[] = {
{0, nullptr, "NotifyMessageToHomeMenuForDebug"},
{1, nullptr, "OpenMainApplication"},
- {10, nullptr, "EmulateButtonEvent"},
+ {10, nullptr, "PerformSystemButtonPressing"},
{20, nullptr, "InvalidateTransitionLayer"},
{30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
{40, nullptr, "GetAppletResourceUsageInfo"},
{100, nullptr, "SetCpuBoostModeForApplet"},
+ {101, nullptr, "CancelCpuBoostModeForApplet"},
{110, nullptr, "PushToAppletBoundChannelForDebug"},
{111, nullptr, "TryPopFromAppletBoundChannelForDebug"},
{120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"},
@@ -271,12 +266,14 @@ ISelfController::ISelfController(Core::System& system,
{16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
{17, nullptr, "SetControllerFirmwareUpdateSection"},
{18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
- {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"},
+ {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
{20, nullptr, "SetDesirableKeyboardLayout"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
{41, nullptr, "IsSystemBufferSharingEnabled"},
{42, nullptr, "GetSystemSharedLayerHandle"},
{43, nullptr, "GetSystemSharedBufferHandle"},
+ {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
+ {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
{51, nullptr, "ApproveToDisplay"},
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
@@ -381,7 +378,11 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
}
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto permission = rp.PopEnum<ScreenshotPermission>();
+ LOG_DEBUG(Service_AM, "called, permission={}", permission);
+
+ screenshot_permission = permission;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -445,7 +446,7 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext&
rb.Push(RESULT_SUCCESS);
}
-void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
+void ISelfController::SetAlbumImageOrientation(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
@@ -465,6 +466,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
rb.Push(*layer_id);
}
+void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ // TODO(Subv): Find out how AM determines the display to use, for now just
+ // create the layer in the Default display.
+ // This calls nn::vi::CreateRecordingLayer() which creates another layer.
+ // Currently we do not support more than 1 layer per display, output 1 layer id for now.
+ // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
+ // side effects.
+ // TODO: Support multiple layers
+ const auto display_id = nvflinger->OpenDisplay("Default");
+ const auto layer_id = nvflinger->CreateLayer(*display_id);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(*layer_id);
+}
+
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -609,6 +628,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
{20, nullptr, "PushToGeneralChannel"},
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
{31, nullptr, "GetReaderLockAccessorEx"},
+ {32, nullptr, "GetWriterLockAccessorEx"},
{40, nullptr, "GetCradleFwVersion"},
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
@@ -623,11 +643,15 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
{64, nullptr, "SetTvPowerStateMatchingMode"},
{65, nullptr, "GetApplicationIdByContentActionName"},
{66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
+ {67, nullptr, "CancelCpuBoostMode"},
{80, nullptr, "PerformSystemButtonPressingIfInFocus"},
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
{91, nullptr, "GetCurrentPerformanceConfiguration"},
+ {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
{200, nullptr, "GetOperationModeSystemInfo"},
{300, nullptr, "GetSettingsPlatformRegion"},
+ {400, nullptr, "ActivateMigrationService"},
+ {401, nullptr, "DeactivateMigrationService"},
};
// clang-format on
@@ -678,27 +702,21 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.PushEnum(VrMode::Disabled);
+ rb.Push(vr_mode_state);
}
void ICommonStateGetter::SetVrModeEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto is_vr_mode_enabled = rp.Pop<bool>();
+ vr_mode_state = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called. is_vr_mode_enabled={}", is_vr_mode_enabled);
+ LOG_WARNING(Service_AM, "VR Mode is {}", vr_mode_state ? "on" : "off");
IPC::ResponseBuilder rb{ctx, 2};
- if (!is_vr_mode_enabled) {
- rb.Push(RESULT_SUCCESS);
- } else {
- // TODO: Find better error code for this
- UNIMPLEMENTED_MSG("is_vr_mode_enabled={}", is_vr_mode_enabled);
- rb.Push(RESULT_UNKNOWN);
- }
+ rb.Push(RESULT_SUCCESS);
}
void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) {
@@ -733,16 +751,16 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- if (Settings::values.use_docked_mode) {
+ if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
} else {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
}
}
@@ -806,7 +824,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
- const bool use_docked_mode{Settings::values.use_docked_mode};
+ const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
IPC::ResponseBuilder rb{ctx, 3};
@@ -835,6 +853,7 @@ public:
{25, nullptr, "Terminate"},
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
+ {60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"},
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
{102, nullptr, "PushExtraStorage"},
@@ -845,7 +864,7 @@ public:
{110, nullptr, "NeedsToExitProcess"},
{120, nullptr, "GetLibraryAppletInfo"},
{150, nullptr, "RequestForAppletToGetForeground"},
- {160, nullptr, "GetIndirectLayerConsumerHandle"},
+ {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
};
// clang-format on
@@ -903,7 +922,7 @@ private:
void PopOutData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
- const auto storage = applet->GetBroker().PopNormalDataToGame();
+ auto storage = applet->GetBroker().PopNormalDataToGame();
if (storage == nullptr) {
LOG_ERROR(Service_AM,
"storage is a nullptr. There is no data in the current normal channel");
@@ -934,7 +953,7 @@ private:
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
- const auto storage = applet->GetBroker().PopInteractiveDataToGame();
+ auto storage = applet->GetBroker().PopInteractiveDataToGame();
if (storage == nullptr) {
LOG_ERROR(Service_AM,
"storage is a nullptr. There is no data in the current interactive channel");
@@ -964,6 +983,18 @@ private:
rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
}
+ void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
+ // actually used anywhere
+ constexpr u64 handle = 0xdeadbeef;
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(handle);
+ }
+
std::shared_ptr<Applets::Applet> applet;
};
@@ -1135,10 +1166,12 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
+ {27, nullptr, "CreateCacheStorage"},
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
+ {34, nullptr, "SelectApplicationLicense"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -1148,6 +1181,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
{70, nullptr, "RequestToShutdown"},
{71, nullptr, "RequestToReboot"},
+ {72, nullptr, "RequestToSleep"},
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
{90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
{100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
@@ -1158,13 +1192,17 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{120, nullptr, "ExecuteProgram"},
{121, nullptr, "ClearUserChannel"},
{122, nullptr, "UnpopToUserChannel"},
+ {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
+ {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
- {140, nullptr, "GetFriendInvitationStorageChannelEvent"},
+ {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
{141, nullptr, "TryPopFromFriendInvitationStorageChannel"},
{150, nullptr, "GetNotificationStorageChannelEvent"},
{151, nullptr, "TryPopFromNotificationStorageChannel"},
{160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
{170, nullptr, "SetHdcpAuthenticationActivated"},
+ {180, nullptr, "GetLaunchRequiredVersion"},
+ {181, nullptr, "UpgradeLaunchRequiredVersion"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, nullptr, "PrepareForJit"},
@@ -1176,6 +1214,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
auto& kernel = system.Kernel();
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
kernel, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
+
+ friend_invitation_storage_channel_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, "IApplicationFunctions:FriendInvitationStorageChannelEvent");
}
IApplicationFunctions::~IApplicationFunctions() = default;
@@ -1307,12 +1348,12 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
- FileSys::SaveDataDescriptor descriptor{};
- descriptor.title_id = system.CurrentProcess()->GetTitleID();
- descriptor.user_id = user_id;
- descriptor.type = FileSys::SaveDataType::SaveData;
+ FileSys::SaveDataAttribute attribute{};
+ attribute.title_id = system.CurrentProcess()->GetTitleID();
+ attribute.user_id = user_id;
+ attribute.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(
- FileSys::SaveDataSpaceId::NandUser, descriptor);
+ FileSys::SaveDataSpaceId::NandUser, attribute);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res.Code());
@@ -1333,12 +1374,37 @@ void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
}
void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called");
+
+ std::array<u8, 0x10> version_string{};
+
+ const auto res = [this] {
+ const auto title_id = system.CurrentProcess()->GetTitleID();
+
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
+ auto res = pm.GetControlMetadata();
+ if (res.first != nullptr) {
+ return res;
+ }
+
+ const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ return pm_update.GetControlMetadata();
+ }();
+
+ if (res.first != nullptr) {
+ const auto& version = res.first->GetVersionString();
+ std::copy(version.begin(), version.end(), version_string.begin());
+ } else {
+ constexpr char default_version[]{"1.0.0"};
+ std::memcpy(version_string.data(), default_version, sizeof(default_version));
+ }
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(1);
- rb.Push<u64>(0);
+ rb.PushRaw(version_string);
}
void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
@@ -1348,9 +1414,23 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
// Get supported languages from NACP, if possible
// Default to 0 (all languages supported)
u32 supported_languages = 0;
- FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
- const auto res = pm.GetControlMetadata();
+ const auto res = [this] {
+ const auto title_id = system.CurrentProcess()->GetTitleID();
+
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
+ auto res = pm.GetControlMetadata();
+ if (res.first != nullptr) {
+ return res;
+ }
+
+ const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ return pm_update.GetControlMetadata();
+ }();
+
if (res.first != nullptr) {
supported_languages = res.first->GetSupportedLanguages();
}
@@ -1482,6 +1562,14 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
rb.Push<u32>(0);
}
+void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(previous_program_index);
+}
+
void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -1490,6 +1578,14 @@ void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestCon
rb.PushCopyObjects(gpu_error_detected_event.readable);
}
+void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(friend_invitation_storage_channel_event.readable);
+}
+
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());
@@ -1504,14 +1600,15 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
std::make_shared<TCAP>()->InstallAsService(service_manager);
}
-IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
+IHomeMenuFunctions::IHomeMenuFunctions(Kernel::KernelCore& kernel)
+ : ServiceFramework("IHomeMenuFunctions"), kernel(kernel) {
// clang-format off
static const FunctionInfo functions[] = {
{10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
{11, nullptr, "LockForeground"},
{12, nullptr, "UnlockForeground"},
{20, nullptr, "PopFromGeneralChannel"},
- {21, nullptr, "GetPopFromGeneralChannelEvent"},
+ {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"},
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
{31, nullptr, "GetWriterLockAccessorEx"},
{100, nullptr, "PopRequestLaunchApplicationForDebug"},
@@ -1521,6 +1618,9 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
// clang-format on
RegisterHandlers(functions);
+
+ pop_from_general_channel_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, "IHomeMenuFunctions:PopFromGeneralChannelEvent");
}
IHomeMenuFunctions::~IHomeMenuFunctions() = default;
@@ -1532,6 +1632,14 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
rb.Push(RESULT_SUCCESS);
}
+void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(pop_from_general_channel_event.readable);
+}
+
IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
// clang-format off
static const FunctionInfo functions[] = {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 53cfce10f..bcc06affe 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -138,8 +138,9 @@ private:
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
- void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
+ void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
+ void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx);
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
@@ -148,6 +149,12 @@ private:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
+ enum class ScreenshotPermission : u32 {
+ Inherit = 0,
+ Enable = 1,
+ Disable = 2,
+ };
+
Core::System& system;
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
@@ -156,6 +163,7 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
+ ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -191,6 +199,7 @@ private:
Core::System& system;
std::shared_ptr<AppletMessageQueue> msg_queue;
+ bool vr_mode_state{};
};
class IStorageImpl {
@@ -279,21 +288,29 @@ private:
void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
+ void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
+ void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
bool launch_popped_application_specific = false;
bool launch_popped_account_preselect = false;
+ s32 previous_program_index{-1};
Kernel::EventPair gpu_error_detected_event;
+ Kernel::EventPair friend_invitation_storage_channel_event;
Core::System& system;
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
public:
- IHomeMenuFunctions();
+ explicit IHomeMenuFunctions(Kernel::KernelCore& kernel);
~IHomeMenuFunctions() override;
private:
void RequestToGetForeground(Kernel::HLERequestContext& ctx);
+ void GetPopFromGeneralChannelEvent(Kernel::HLERequestContext& ctx);
+
+ Kernel::EventPair pop_from_general_channel_event;
+ Kernel::KernelCore& kernel;
};
class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index e454b77d8..be23ca747 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -3,8 +3,8 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -202,7 +202,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IHomeMenuFunctions>();
+ rb.PushIpcInterface<IHomeMenuFunctions>(system.Kernel());
}
void GetGlobalStateController(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index c3261f3e6..2b626bb40 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,6 +5,7 @@
#include <cstring>
#include "common/assert.h"
#include "core/core.h"
+#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/profile_select.h"
@@ -15,6 +16,7 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/am/applets/controller.h"
#include "core/hle/service/am/applets/error.h"
#include "core/hle/service/am/applets/general_backend.h"
#include "core/hle/service/am/applets/profile_select.h"
@@ -140,14 +142,14 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
-AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
+AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce,
+ ErrorApplet error, ParentalControlsApplet parental_controls,
PhotoViewer photo_viewer, ProfileSelect profile_select,
- SoftwareKeyboard software_keyboard, WebBrowser web_browser,
- ECommerceApplet e_commerce)
- : parental_controls{std::move(parental_controls)}, error{std::move(error)},
- photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)},
- software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)},
- e_commerce{std::move(e_commerce)} {}
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser)
+ : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)},
+ parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)},
+ profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)},
+ web_browser{std::move(web_browser)} {}
AppletFrontendSet::~AppletFrontendSet() = default;
@@ -164,20 +166,37 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
}
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
- if (set.parental_controls != nullptr)
- frontend.parental_controls = std::move(set.parental_controls);
- if (set.error != nullptr)
+ if (set.controller != nullptr) {
+ frontend.controller = std::move(set.controller);
+ }
+
+ if (set.e_commerce != nullptr) {
+ frontend.e_commerce = std::move(set.e_commerce);
+ }
+
+ if (set.error != nullptr) {
frontend.error = std::move(set.error);
- if (set.photo_viewer != nullptr)
+ }
+
+ if (set.parental_controls != nullptr) {
+ frontend.parental_controls = std::move(set.parental_controls);
+ }
+
+ if (set.photo_viewer != nullptr) {
frontend.photo_viewer = std::move(set.photo_viewer);
- if (set.profile_select != nullptr)
+ }
+
+ if (set.profile_select != nullptr) {
frontend.profile_select = std::move(set.profile_select);
- if (set.software_keyboard != nullptr)
+ }
+
+ if (set.software_keyboard != nullptr) {
frontend.software_keyboard = std::move(set.software_keyboard);
- if (set.web_browser != nullptr)
+ }
+
+ if (set.web_browser != nullptr) {
frontend.web_browser = std::move(set.web_browser);
- if (set.e_commerce != nullptr)
- frontend.e_commerce = std::move(set.e_commerce);
+ }
}
void AppletManager::SetDefaultAppletFrontendSet() {
@@ -186,15 +205,24 @@ void AppletManager::SetDefaultAppletFrontendSet() {
}
void AppletManager::SetDefaultAppletsIfMissing() {
- if (frontend.parental_controls == nullptr) {
- frontend.parental_controls =
- std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
+ if (frontend.controller == nullptr) {
+ frontend.controller =
+ std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
+ }
+
+ if (frontend.e_commerce == nullptr) {
+ frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
}
if (frontend.error == nullptr) {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
+ if (frontend.parental_controls == nullptr) {
+ frontend.parental_controls =
+ std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
+ }
+
if (frontend.photo_viewer == nullptr) {
frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
}
@@ -211,10 +239,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.web_browser == nullptr) {
frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
}
-
- if (frontend.e_commerce == nullptr) {
- frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
- }
}
void AppletManager::ClearAll() {
@@ -225,6 +249,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
switch (id) {
case AppletId::Auth:
return std::make_shared<Auth>(system, *frontend.parental_controls);
+ case AppletId::Controller:
+ return std::make_shared<Controller>(system, *frontend.controller);
case AppletId::Error:
return std::make_shared<Error>(system, *frontend.error);
case AppletId::ProfileSelect:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index e75be86a2..a1f4cf897 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -17,6 +17,7 @@ class System;
}
namespace Core::Frontend {
+class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
class ParentalControlsApplet;
@@ -155,19 +156,20 @@ protected:
};
struct AppletFrontendSet {
- using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
+ using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
+ using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
+ using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
- using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
AppletFrontendSet();
- AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
- PhotoViewer photo_viewer, ProfileSelect profile_select,
- SoftwareKeyboard software_keyboard, WebBrowser web_browser,
- ECommerceApplet e_commerce);
+ AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error,
+ ParentalControlsApplet parental_controls, PhotoViewer photo_viewer,
+ ProfileSelect profile_select, SoftwareKeyboard software_keyboard,
+ WebBrowser web_browser);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -176,13 +178,14 @@ struct AppletFrontendSet {
AppletFrontendSet(AppletFrontendSet&&) noexcept;
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
- ParentalControlsApplet parental_controls;
+ ControllerApplet controller;
+ ECommerceApplet e_commerce;
ErrorApplet error;
+ ParentalControlsApplet parental_controls;
PhotoViewer photo_viewer;
ProfileSelect profile_select;
SoftwareKeyboard software_keyboard;
WebBrowser web_browser;
- ECommerceApplet e_commerce;
};
class AppletManager {
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
new file mode 100644
index 000000000..3ca63f020
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -0,0 +1,252 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstring>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/frontend/applets/controller.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/controller.h"
+#include "core/hle/service/hid/controllers/npad.h"
+
+namespace Service::AM::Applets {
+
+// This error code (0x183ACA) is thrown when the applet fails to initialize.
+[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
+// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
+[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
+
+static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
+ ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
+ std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
+ HID::Controller_NPad::NpadStyleSet npad_style_set;
+ npad_style_set.raw = private_arg.style_set;
+
+ return {
+ .min_players = std::max(s8(1), header.player_count_min),
+ .max_players = header.player_count_max,
+ .keep_controllers_connected = header.enable_take_over_connection,
+ .enable_single_mode = header.enable_single_mode,
+ .enable_border_color = header.enable_identification_color,
+ .border_colors = identification_colors,
+ .enable_explain_text = enable_text,
+ .explain_text = text,
+ .allow_pro_controller = npad_style_set.pro_controller == 1,
+ .allow_handheld = npad_style_set.handheld == 1,
+ .allow_dual_joycons = npad_style_set.joycon_dual == 1,
+ .allow_left_joycon = npad_style_set.joycon_left == 1,
+ .allow_right_joycon = npad_style_set.joycon_right == 1,
+ };
+}
+
+Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_)
+ : Applet{system_.Kernel()}, frontend(frontend_) {}
+
+Controller::~Controller() = default;
+
+void Controller::Initialize() {
+ Applet::Initialize();
+
+ LOG_INFO(Service_HID, "Initializing Controller Applet.");
+
+ LOG_DEBUG(Service_HID,
+ "Initializing Applet with common_args: arg_version={}, lib_version={}, "
+ "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
+ common_args.arguments_version, common_args.library_version,
+ common_args.play_startup_sound, common_args.size, common_args.system_tick,
+ common_args.theme_color);
+
+ controller_applet_version = ControllerAppletVersion{common_args.library_version};
+
+ const auto private_arg_storage = broker.PopNormalDataToApplet();
+ ASSERT(private_arg_storage != nullptr);
+
+ const auto& private_arg = private_arg_storage->GetData();
+ ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
+
+ std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size());
+ ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
+ "Unknown ControllerSupportArgPrivate revision={} with size={}",
+ controller_applet_version, controller_private_arg.arg_private_size);
+
+ // Some games such as Cave Story+ set invalid values for the ControllerSupportMode.
+ // Defer to arg_size to set the ControllerSupportMode.
+ if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) {
+ switch (controller_private_arg.arg_size) {
+ case sizeof(ControllerSupportArgOld):
+ case sizeof(ControllerSupportArgNew):
+ controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
+ break;
+ case sizeof(ControllerUpdateFirmwareArg):
+ controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate;
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}",
+ controller_private_arg.mode, controller_private_arg.arg_size);
+ controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
+ break;
+ }
+ }
+
+ // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller.
+ // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem.
+ if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) {
+ if (controller_private_arg.flag_1 &&
+ controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) {
+ controller_private_arg.caller = ControllerSupportCaller::System;
+ } else {
+ controller_private_arg.caller = ControllerSupportCaller::Application;
+ }
+ }
+
+ switch (controller_private_arg.mode) {
+ case ControllerSupportMode::ShowControllerSupport:
+ case ControllerSupportMode::ShowControllerStrapGuide: {
+ const auto user_arg_storage = broker.PopNormalDataToApplet();
+ ASSERT(user_arg_storage != nullptr);
+
+ const auto& user_arg = user_arg_storage->GetData();
+ switch (controller_applet_version) {
+ case ControllerAppletVersion::Version3:
+ case ControllerAppletVersion::Version4:
+ case ControllerAppletVersion::Version5:
+ ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
+ std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size());
+ break;
+ case ControllerAppletVersion::Version7:
+ ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
+ std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size());
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
+ controller_applet_version, controller_private_arg.arg_size);
+ ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
+ std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
+ break;
+ }
+ break;
+ }
+ case ControllerSupportMode::ShowControllerFirmwareUpdate: {
+ const auto update_arg_storage = broker.PopNormalDataToApplet();
+ ASSERT(update_arg_storage != nullptr);
+
+ const auto& update_arg = update_arg_storage->GetData();
+ ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg));
+
+ std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size());
+ break;
+ }
+ default: {
+ UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
+ break;
+ }
+ }
+}
+
+bool Controller::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode Controller::GetStatus() const {
+ return status;
+}
+
+void Controller::ExecuteInteractive() {
+ UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+}
+
+void Controller::Execute() {
+ switch (controller_private_arg.mode) {
+ case ControllerSupportMode::ShowControllerSupport: {
+ const auto parameters = [this] {
+ switch (controller_applet_version) {
+ case ControllerAppletVersion::Version3:
+ case ControllerAppletVersion::Version4:
+ case ControllerAppletVersion::Version5:
+ return ConvertToFrontendParameters(
+ controller_private_arg, controller_user_arg_old.header,
+ controller_user_arg_old.enable_explain_text,
+ std::vector<IdentificationColor>(
+ controller_user_arg_old.identification_colors.begin(),
+ controller_user_arg_old.identification_colors.end()),
+ std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
+ controller_user_arg_old.explain_text.end()));
+ case ControllerAppletVersion::Version7:
+ default:
+ return ConvertToFrontendParameters(
+ controller_private_arg, controller_user_arg_new.header,
+ controller_user_arg_new.enable_explain_text,
+ std::vector<IdentificationColor>(
+ controller_user_arg_new.identification_colors.begin(),
+ controller_user_arg_new.identification_colors.end()),
+ std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(),
+ controller_user_arg_new.explain_text.end()));
+ }
+ }();
+
+ is_single_mode = parameters.enable_single_mode;
+
+ LOG_DEBUG(Service_HID,
+ "Controller Parameters: min_players={}, max_players={}, "
+ "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, "
+ "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, "
+ "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}",
+ parameters.min_players, parameters.max_players,
+ parameters.keep_controllers_connected, parameters.enable_single_mode,
+ parameters.enable_border_color, parameters.enable_explain_text,
+ parameters.allow_pro_controller, parameters.allow_handheld,
+ parameters.allow_dual_joycons, parameters.allow_left_joycon,
+ parameters.allow_right_joycon);
+
+ frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters);
+ break;
+ }
+ case ControllerSupportMode::ShowControllerStrapGuide:
+ case ControllerSupportMode::ShowControllerFirmwareUpdate:
+ UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
+ controller_private_arg.mode);
+ [[fallthrough]];
+ default: {
+ ConfigurationComplete();
+ break;
+ }
+ }
+}
+
+void Controller::ConfigurationComplete() {
+ ControllerSupportResultInfo result_info{};
+
+ const auto& players = Settings::values.players.GetValue();
+
+ // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
+ // Otherwise, only count connected players from P1-P8.
+ result_info.player_count =
+ is_single_mode ? 1
+ : static_cast<s8>(std::count_if(
+ players.begin(), players.end() - 2,
+ [](Settings::PlayerInput player) { return player.connected; }));
+
+ result_info.selected_id = HID::Controller_NPad::IndexToNPad(
+ std::distance(players.begin(),
+ std::find_if(players.begin(), players.end(),
+ [](Settings::PlayerInput player) { return player.connected; })));
+
+ result_info.result = 0;
+
+ LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}",
+ result_info.player_count, result_info.selected_id, result_info.result);
+
+ complete = true;
+ out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo));
+ std::memcpy(out_data.data(), &result_info, out_data.size());
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data)));
+ broker.SignalStateChanged();
+}
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h
new file mode 100644
index 000000000..a7a1f2b65
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.h
@@ -0,0 +1,135 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::AM::Applets {
+
+using IdentificationColor = std::array<u8, 4>;
+using ExplainText = std::array<char, 0x81>;
+
+enum class ControllerAppletVersion : u32_le {
+ Version3 = 0x3, // 1.0.0 - 2.3.0
+ Version4 = 0x4, // 3.0.0 - 5.1.0
+ Version5 = 0x5, // 6.0.0 - 7.0.1
+ Version7 = 0x7, // 8.0.0+
+};
+
+enum class ControllerSupportMode : u8 {
+ ShowControllerSupport,
+ ShowControllerStrapGuide,
+ ShowControllerFirmwareUpdate,
+
+ MaxControllerSupportMode,
+};
+
+enum class ControllerSupportCaller : u8 {
+ Application,
+ System,
+
+ MaxControllerSupportCaller,
+};
+
+struct ControllerSupportArgPrivate {
+ u32 arg_private_size{};
+ u32 arg_size{};
+ bool flag_0{};
+ bool flag_1{};
+ ControllerSupportMode mode{};
+ ControllerSupportCaller caller{};
+ u32 style_set{};
+ u32 joy_hold_type{};
+};
+static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
+ "ControllerSupportArgPrivate has incorrect size.");
+
+struct ControllerSupportArgHeader {
+ s8 player_count_min{};
+ s8 player_count_max{};
+ bool enable_take_over_connection{};
+ bool enable_left_justify{};
+ bool enable_permit_joy_dual{};
+ bool enable_single_mode{};
+ bool enable_identification_color{};
+};
+static_assert(sizeof(ControllerSupportArgHeader) == 0x7,
+ "ControllerSupportArgHeader has incorrect size.");
+
+// LibraryAppletVersion 0x3, 0x4, 0x5
+struct ControllerSupportArgOld {
+ ControllerSupportArgHeader header{};
+ std::array<IdentificationColor, 4> identification_colors{};
+ bool enable_explain_text{};
+ std::array<ExplainText, 4> explain_text{};
+};
+static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
+ "ControllerSupportArgOld has incorrect size.");
+
+// LibraryAppletVersion 0x7
+struct ControllerSupportArgNew {
+ ControllerSupportArgHeader header{};
+ std::array<IdentificationColor, 8> identification_colors{};
+ bool enable_explain_text{};
+ std::array<ExplainText, 8> explain_text{};
+};
+static_assert(sizeof(ControllerSupportArgNew) == 0x430,
+ "ControllerSupportArgNew has incorrect size.");
+
+struct ControllerUpdateFirmwareArg {
+ bool enable_force_update{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4,
+ "ControllerUpdateFirmwareArg has incorrect size.");
+
+struct ControllerSupportResultInfo {
+ s8 player_count{};
+ INSERT_PADDING_BYTES(3);
+ u32 selected_id{};
+ u32 result{};
+};
+static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
+ "ControllerSupportResultInfo has incorrect size.");
+
+class Controller final : public Applet {
+public:
+ explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_);
+ ~Controller() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void ConfigurationComplete();
+
+private:
+ const Core::Frontend::ControllerApplet& frontend;
+
+ ControllerAppletVersion controller_applet_version;
+ ControllerSupportArgPrivate controller_private_arg;
+ ControllerSupportArgOld controller_user_arg_old;
+ ControllerSupportArgNew controller_user_arg_new;
+ ControllerUpdateFirmwareArg controller_update_arg;
+ bool complete{false};
+ ResultCode status{RESULT_SUCCESS};
+ bool is_single_mode{false};
+ std::vector<u8> out_data;
+};
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 54e63c138..bdeb0737a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -13,11 +13,23 @@
namespace Service::AM::Applets {
+namespace {
+enum class Request : u32 {
+ Finalize = 0x4,
+ SetUserWordInfo = 0x6,
+ SetCustomizeDic = 0x7,
+ Calc = 0xa,
+ SetCustomizedDictionaries = 0xb,
+ UnsetCustomizedDictionaries = 0xc,
+ UnknownD = 0xd,
+ UnknownE = 0xe,
+};
+constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8;
constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
constexpr bool INTERACTIVE_STATUS_OK = false;
-
+} // Anonymous namespace
static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
KeyboardConfig config, std::u16string initial_text) {
Core::Frontend::SoftwareKeyboardParameters params{};
@@ -30,7 +42,7 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
config.sub_text.size());
params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
config.guide_text.size());
- params.initial_text = initial_text;
+ params.initial_text = std::move(initial_text);
params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
params.password = static_cast<bool>(config.is_password);
params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
@@ -47,6 +59,7 @@ SoftwareKeyboard::~SoftwareKeyboard() = default;
void SoftwareKeyboard::Initialize() {
complete = false;
+ is_inline = false;
initial_text.clear();
final_data.clear();
@@ -56,11 +69,16 @@ void SoftwareKeyboard::Initialize() {
ASSERT(keyboard_config_storage != nullptr);
const auto& keyboard_config = keyboard_config_storage->GetData();
+ if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) {
+ is_inline = true;
+ return;
+ }
+
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
const auto work_buffer_storage = broker.PopNormalDataToApplet();
- ASSERT(work_buffer_storage != nullptr);
+ ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; });
const auto& work_buffer = work_buffer_storage->GetData();
if (config.initial_string_size == 0)
@@ -87,16 +105,31 @@ void SoftwareKeyboard::ExecuteInteractive() {
const auto storage = broker.PopInteractiveDataToApplet();
ASSERT(storage != nullptr);
const auto data = storage->GetData();
- const auto status = static_cast<bool>(data[0]);
-
- if (status == INTERACTIVE_STATUS_OK) {
- complete = true;
+ if (!is_inline) {
+ const auto status = static_cast<bool>(data[0]);
+ if (status == INTERACTIVE_STATUS_OK) {
+ complete = true;
+ } else {
+ std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
+ std::memcpy(string.data(), data.data() + 4, string.size() * 2);
+ frontend.SendTextCheckDialog(
+ Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
+ [this] { broker.SignalStateChanged(); });
+ }
} else {
- std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
- std::memcpy(string.data(), data.data() + 4, string.size() * 2);
- frontend.SendTextCheckDialog(
- Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
- [this] { broker.SignalStateChanged(); });
+ Request request{};
+ std::memcpy(&request, data.data(), sizeof(Request));
+
+ switch (request) {
+ case Request::Calc: {
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{1}));
+ broker.SignalStateChanged();
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("Request {:X} is not implemented", request);
+ break;
+ }
}
}
@@ -108,9 +141,10 @@ void SoftwareKeyboard::Execute() {
}
const auto parameters = ConvertToFrontendParameters(config, initial_text);
-
- frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
- parameters);
+ if (!is_inline) {
+ frontend.RequestText(
+ [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters);
+ }
}
void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index ef4801fc6..5a3824b5a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -78,6 +78,7 @@ private:
KeyboardConfig config;
std::u16string initial_text;
bool complete = false;
+ bool is_inline = false;
std::vector<u8> final_data;
};
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9f30e167d..efe595c4f 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -293,8 +293,8 @@ void WebBrowser::Finalize() {
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
broker.SignalStateChanged();
- if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {
- FileUtil::DeleteDirRecursively(temporary_dir);
+ if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) {
+ Common::FS::DeleteDirRecursively(temporary_dir);
}
}
@@ -452,10 +452,10 @@ void WebBrowser::InitializeOffline() {
};
temporary_dir =
- FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" +
- WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
- FileUtil::DirectorySeparator::PlatformDefault);
- FileUtil::DeleteDirRecursively(temporary_dir);
+ Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
+ "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
+ Common::FS::DirectorySeparator::PlatformDefault);
+ Common::FS::DeleteDirRecursively(temporary_dir);
u64 title_id = 0; // 0 corresponds to current process
ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
@@ -492,8 +492,8 @@ void WebBrowser::InitializeOffline() {
}
filename =
- FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
- FileUtil::DirectorySeparator::PlatformDefault);
+ Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
+ Common::FS::DirectorySeparator::PlatformDefault);
}
void WebBrowser::ExecuteShop() {
@@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() {
}
void WebBrowser::ExecuteOffline() {
- frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
+ frontend.OpenPageLocal(
+ filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
}
} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp
index 003ee8667..f27729ce7 100644
--- a/src/core/hle/service/am/spsm.cpp
+++ b/src/core/hle/service/am/spsm.cpp
@@ -10,17 +10,17 @@ SPSM::SPSM() : ServiceFramework{"spsm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetState"},
- {1, nullptr, "SleepSystemAndWaitAwake"},
- {2, nullptr, "Unknown1"},
- {3, nullptr, "Unknown2"},
+ {1, nullptr, "EnterSleep"},
+ {2, nullptr, "GetLastWakeReason"},
+ {3, nullptr, "Shutdown"},
{4, nullptr, "GetNotificationMessageEventHandle"},
- {5, nullptr, "Unknown3"},
- {6, nullptr, "Unknown4"},
- {7, nullptr, "Unknown5"},
+ {5, nullptr, "ReceiveNotificationMessage"},
+ {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"},
+ {7, nullptr, "ResetEventLog"},
{8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"},
{9, nullptr, "ChangeHomeButtonLongPressingTime"},
- {10, nullptr, "Unknown6"},
- {11, nullptr, "Unknown7"},
+ {10, nullptr, "PutErrorState"},
+ {11, nullptr, "InvalidateCurrentHomeButtonPressing"},
};
// clang-format on
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 4227a4adf..173b36da4 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -6,6 +6,7 @@
#include <numeric>
#include <vector>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
@@ -60,6 +61,7 @@ AOC_U::AOC_U(Core::System& system)
{6, nullptr, "PrepareAddOnContentByApplicationId"},
{7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
{8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
+ {9, nullptr, "GetAddOnContentLostErrorCode"},
{100, nullptr, "CreateEcPurchasedEventManager"},
{101, nullptr, "CreatePermanentEcPurchasedEventManager"},
};
@@ -162,7 +164,8 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
const auto title_id = system.CurrentProcess()->GetTitleID();
- FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto res = pm.GetControlMetadata();
if (res.first == nullptr) {
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 85bbf5988..e2d8f0027 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/apm/interface.h"
diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp
index 25a886238..ce993bad3 100644
--- a/src/core/hle/service/apm/controller.cpp
+++ b/src/core/hle/service/apm/controller.cpp
@@ -69,7 +69,8 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
}
PerformanceMode Controller::GetCurrentPerformanceMode() const {
- return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld;
+ return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
+ : PerformanceMode::Handheld;
}
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
index 9e08e5346..6ddb547fb 100644
--- a/src/core/hle/service/audio/audctl.cpp
+++ b/src/core/hle/service/audio/audctl.cpp
@@ -39,6 +39,8 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} {
{25, nullptr, "GetAudioVolumeDataForPlayReport"},
{26, nullptr, "UpdateHeadphoneSettings"},
{27, nullptr, "SetVolumeMappingTableForDev"},
+ {28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
+ {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
};
// clang-format on
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index d7f1d348d..3e2299426 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service::Audio {
@@ -36,11 +39,12 @@ public:
AudInU::AudInU() : ServiceFramework("audin:u") {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "ListAudioIns"},
- {1, nullptr, "OpenAudioIn"},
- {2, nullptr, "Unknown"},
- {3, nullptr, "OpenAudioInAuto"},
- {4, nullptr, "ListAudioInsAuto"},
+ {0, &AudInU::ListAudioIns, "ListAudioIns"},
+ {1, &AudInU::OpenAudioIn, "OpenAudioIn"},
+ {2, &AudInU::ListAudioIns, "ListAudioInsAuto"},
+ {3, &AudInU::OpenAudioIn, "OpenAudioInAuto"},
+ {4, &AudInU::ListAudioInsAutoFiltered, "ListAudioInsAutoFiltered"},
+ {5, &AudInU::OpenAudioInProtocolSpecified, "OpenAudioInProtocolSpecified"},
};
// clang-format on
@@ -49,4 +53,60 @@ AudInU::AudInU() : ServiceFramework("audin:u") {
AudInU::~AudInU() = default;
+void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+ const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName);
+
+ const std::size_t device_count = std::min(count, audio_device_names.size());
+ std::vector<AudioInDeviceName> device_names;
+ device_names.reserve(device_count);
+
+ for (std::size_t i = 0; i < device_count; i++) {
+ const auto& device_name = audio_device_names[i];
+ auto& entry = device_names.emplace_back();
+ device_name.copy(entry.data(), device_name.size());
+ }
+
+ ctx.WriteBuffer(device_names);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u32>(device_names.size()));
+}
+
+void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+ constexpr u32 device_count = 0;
+
+ // Since we don't actually use any other audio input devices, we return 0 devices. Filtered
+ // device listing just omits the default input device
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u32>(device_count));
+}
+
+void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) {
+ AudInOutParams params{};
+ params.channel_count = 2;
+ params.sample_format = SampleFormat::PCM16;
+ params.sample_rate = 48000;
+ params.state = State::Started;
+
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<AudInOutParams>(params);
+ rb.PushIpcInterface<IAudioIn>();
+}
+
+void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Audio, "(STUBBED) called");
+ OpenInOutImpl(ctx);
+}
+
+void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Audio, "(STUBBED) called");
+ OpenInOutImpl(ctx);
+}
+
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h
index 0538b9560..a599f4a64 100644
--- a/src/core/hle/service/audio/audin_u.h
+++ b/src/core/hle/service/audio/audin_u.h
@@ -16,6 +16,35 @@ class AudInU final : public ServiceFramework<AudInU> {
public:
explicit AudInU();
~AudInU() override;
+
+private:
+ enum class SampleFormat : u32_le {
+ PCM16 = 2,
+ };
+
+ enum class State : u32_le {
+ Started = 0,
+ Stopped = 1,
+ };
+
+ struct AudInOutParams {
+ u32_le sample_rate{};
+ u32_le channel_count{};
+ SampleFormat sample_format{};
+ State state{};
+ };
+ static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size");
+
+ using AudioInDeviceName = std::array<char, 256>;
+ static constexpr std::array<std::string_view, 1> audio_device_names{{
+ "BuiltInHeadset",
+ }};
+
+ void ListAudioIns(Kernel::HLERequestContext& ctx);
+ void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx);
+ void OpenInOutImpl(Kernel::HLERequestContext& ctx);
+ void OpenAudioIn(Kernel::HLERequestContext& ctx);
+ void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 4fb2cbc4b..9b4910e53 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -71,7 +71,7 @@ public:
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
audio_params.channel_count, std::move(unique_name),
- [=]() { buffer_event.writable->Signal(); });
+ [this] { buffer_event.writable->Signal(); });
}
private:
@@ -206,11 +206,11 @@ private:
AudioCore::StreamPtr stream;
std::string device_name;
- [[maybe_unused]] AudoutParams audio_params {};
+ [[maybe_unused]] AudoutParams audio_params{};
/// This is the event handle used to check if the audio buffer was released
Kernel::EventPair buffer_event;
- Memory::Memory& main_memory;
+ Core::Memory::Memory& main_memory;
};
AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} {
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 82a5dbf14..a2d3ded7b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -26,7 +26,7 @@ namespace Service::Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params,
+ explicit IAudioRenderer(Core::System& system, AudioCommon::AudioRendererParameter audren_params,
const std::size_t instance_number)
: ServiceFramework("IAudioRenderer") {
// clang-format off
@@ -92,11 +92,17 @@ private:
}
void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
+
+ std::vector<u8> output_params(ctx.GetWriteBufferSize());
+ auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
+
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(output_params);
+ }
- ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ rb.Push(result);
}
void Start(Kernel::HLERequestContext& ctx) {
@@ -129,7 +135,7 @@ private:
LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
rendering_time_limit_percent);
- ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
+ ASSERT(rendering_time_limit_percent <= 100);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -252,8 +258,6 @@ private:
}
void GetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
@@ -343,7 +347,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
OpenAudioRendererImpl(ctx);
}
-static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) {
+static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
// +1 represents the final mix.
return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
1;
@@ -372,7 +376,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
constexpr u64 upsampler_manager_size = 0x48;
// Calculates the part of the size that relates to mix buffers.
- const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
// As of 8.0.0 this is the maximum on voice channels.
constexpr u64 max_voice_channels = 6;
@@ -394,7 +398,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the portion of the size related to the mix data (and the sorting thereof).
- const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
// The size of the mixing info data structure.
constexpr u64 mix_info_size = 0x940;
@@ -444,7 +448,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to voice channel info.
- const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
constexpr u64 voice_info_size = 0x220;
constexpr u64 voice_resource_size = 0xD0;
@@ -458,7 +462,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to memory pools.
- const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
const u64 memory_pool_info_size = 0x20;
return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
@@ -466,7 +470,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
// Calculates the part of the size related to the splitter context.
const auto calculate_splitter_context_size =
- [](const AudioCore::AudioRendererParameter& params) -> u64 {
+ [](const AudioCommon::AudioRendererParameter& params) -> u64 {
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
return 0;
}
@@ -485,27 +489,29 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to the upsampler info.
- const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) {
- constexpr u64 upsampler_info_size = 0x280;
- // Yes, using the buffer size over info alignment size is intentional here.
- return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count),
- buffer_alignment_size);
- };
+ const auto calculate_upsampler_info_size =
+ [](const AudioCommon::AudioRendererParameter& params) {
+ constexpr u64 upsampler_info_size = 0x280;
+ // Yes, using the buffer size over info alignment size is intentional here.
+ return Common::AlignUp(upsampler_info_size *
+ (u64{params.submix_count} + params.sink_count),
+ buffer_alignment_size);
+ };
// Calculates the part of the size related to effect info.
- const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
constexpr u64 effect_info_size = 0x2B0;
return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
};
// Calculates the part of the size related to audio sink info.
- const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
const u64 sink_info_size = 0x170;
return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
};
// Calculates the part of the size related to voice state info.
- const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
const u64 voice_state_size = 0x100;
const u64 additional_size = buffer_alignment_size - 1;
return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
@@ -513,7 +519,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to performance statistics.
- const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
// Extra size value appended to the end of the calculation.
constexpr u64 appended = 128;
@@ -540,79 +546,81 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size that relates to the audio command buffer.
- const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) {
- constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
+ const auto calculate_command_buffer_size =
+ [](const AudioCommon::AudioRendererParameter& params) {
+ constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
- if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
- constexpr u64 command_buffer_size = 0x18000;
+ if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
+ constexpr u64 command_buffer_size = 0x18000;
- return command_buffer_size + alignment;
- }
+ return command_buffer_size + alignment;
+ }
- // When the variadic command buffer is supported, this means
- // the command generator for the audio renderer can issue commands
- // that are (as one would expect), variable in size. So what we need to do
- // is determine the maximum possible size for a few command data structures
- // then multiply them by the amount of present commands indicated by the given
- // respective audio parameters.
+ // When the variadic command buffer is supported, this means
+ // the command generator for the audio renderer can issue commands
+ // that are (as one would expect), variable in size. So what we need to do
+ // is determine the maximum possible size for a few command data structures
+ // then multiply them by the amount of present commands indicated by the given
+ // respective audio parameters.
- constexpr u64 max_biquad_filters = 2;
- constexpr u64 max_mix_buffers = 24;
+ constexpr u64 max_biquad_filters = 2;
+ constexpr u64 max_mix_buffers = 24;
- constexpr u64 biquad_filter_command_size = 0x2C;
+ constexpr u64 biquad_filter_command_size = 0x2C;
- constexpr u64 depop_mix_command_size = 0x24;
- constexpr u64 depop_setup_command_size = 0x50;
+ constexpr u64 depop_mix_command_size = 0x24;
+ constexpr u64 depop_setup_command_size = 0x50;
- constexpr u64 effect_command_max_size = 0x540;
+ constexpr u64 effect_command_max_size = 0x540;
- constexpr u64 mix_command_size = 0x1C;
- constexpr u64 mix_ramp_command_size = 0x24;
- constexpr u64 mix_ramp_grouped_command_size = 0x13C;
+ constexpr u64 mix_command_size = 0x1C;
+ constexpr u64 mix_ramp_command_size = 0x24;
+ constexpr u64 mix_ramp_grouped_command_size = 0x13C;
- constexpr u64 perf_command_size = 0x28;
+ constexpr u64 perf_command_size = 0x28;
- constexpr u64 sink_command_size = 0x130;
+ constexpr u64 sink_command_size = 0x130;
- constexpr u64 submix_command_max_size =
- depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
+ constexpr u64 submix_command_max_size =
+ depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
- constexpr u64 volume_command_size = 0x1C;
- constexpr u64 volume_ramp_command_size = 0x20;
+ constexpr u64 volume_command_size = 0x1C;
+ constexpr u64 volume_ramp_command_size = 0x20;
- constexpr u64 voice_biquad_filter_command_size =
- biquad_filter_command_size * max_biquad_filters;
- constexpr u64 voice_data_command_size = 0x9C;
- const u64 voice_command_max_size =
- (params.splitter_count * depop_setup_command_size) +
- (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size +
- mix_ramp_grouped_command_size);
+ constexpr u64 voice_biquad_filter_command_size =
+ biquad_filter_command_size * max_biquad_filters;
+ constexpr u64 voice_data_command_size = 0x9C;
+ const u64 voice_command_max_size =
+ (params.splitter_count * depop_setup_command_size) +
+ (voice_data_command_size + voice_biquad_filter_command_size +
+ volume_ramp_command_size + mix_ramp_grouped_command_size);
- // Now calculate the individual elements that comprise the size and add them together.
- const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+ // Now calculate the individual elements that comprise the size and add them together.
+ const u64 effect_commands_size = params.effect_count * effect_command_max_size;
- const u64 final_mix_commands_size =
- depop_mix_command_size + volume_command_size * max_mix_buffers;
+ const u64 final_mix_commands_size =
+ depop_mix_command_size + volume_command_size * max_mix_buffers;
- const u64 perf_commands_size =
- perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+ const u64 perf_commands_size =
+ perf_command_size *
+ (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
- const u64 sink_commands_size = params.sink_count * sink_command_size;
+ const u64 sink_commands_size = params.sink_count * sink_command_size;
- const u64 splitter_commands_size =
- params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+ const u64 splitter_commands_size =
+ params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
- const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+ const u64 submix_commands_size = params.submix_count * submix_command_max_size;
- const u64 voice_commands_size = params.voice_count * voice_command_max_size;
+ const u64 voice_commands_size = params.voice_count * voice_command_max_size;
- return effect_commands_size + final_mix_commands_size + perf_commands_size +
- sink_commands_size + splitter_commands_size + submix_commands_size +
- voice_commands_size + alignment;
- };
+ return effect_commands_size + final_mix_commands_size + perf_commands_size +
+ sink_commands_size + splitter_commands_size + submix_commands_size +
+ voice_commands_size + alignment;
+ };
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
+ const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
u64 size = 0;
size += calculate_mix_buffer_sizes(params);
@@ -678,7 +686,7 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c
void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
+ const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index d19513cbb..f1d81602c 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -92,7 +92,7 @@ private:
if (performance) {
rb.Push<u64>(*performance);
}
- ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
+ ctx.WriteBuffer(samples);
}
bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index def3410cc..174388445 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -84,7 +84,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
void ProgressServiceBackend::SignalUpdate() const {
if (need_hle_lock) {
- std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ std::lock_guard lock(HLE::g_hle_lock);
event.writable->Signal();
} else {
event.writable->Signal();
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index f589864ee..3b6f7498e 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -4,8 +4,8 @@
#include <fmt/ostream.h>
#include <httplib.h>
-#include <json.hpp>
#include <mbedtls/sha256.h>
+#include <nlohmann/json.hpp>
#include "common/hex_util.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
@@ -18,6 +18,7 @@
#include "core/hle/service/bcat/backend/boxcat.h"
#include "core/settings.h"
+namespace Service::BCAT {
namespace {
// Prevents conflicts with windows macro called CreateFile
@@ -30,10 +31,6 @@ bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
return dir->DeleteFile(name);
}
-} // Anonymous namespace
-
-namespace Service::BCAT {
-
constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
@@ -90,16 +87,14 @@ constexpr u32 PORT = 443;
constexpr u32 TIMEOUT_SECONDS = 30;
[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
-namespace {
-
std::string GetBINFilePath(u64 title_id) {
return fmt::format("{}bcat/{:016X}/launchparam.bin",
- FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
+ Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
}
std::string GetZIPFilePath(u64 title_id) {
return fmt::format("{}bcat/{:016X}/data.zip",
- FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
+ Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id);
}
// If the error is something the user should know about (build ID mismatch, bad client version),
@@ -201,7 +196,9 @@ private:
const std::string& content_type_name) {
if (client == nullptr) {
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT);
- client->set_timeout_sec(timeout_seconds);
+ client->set_connection_timeout(timeout_seconds);
+ client->set_read_timeout(timeout_seconds);
+ client->set_write_timeout(timeout_seconds);
}
httplib::Headers headers{
@@ -210,8 +207,8 @@ private:
{std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
};
- if (FileUtil::Exists(path)) {
- FileUtil::IOFile file{path, "rb"};
+ if (Common::FS::Exists(path)) {
+ Common::FS::IOFile file{path, "rb"};
if (file.IsOpen()) {
std::vector<u8> bytes(file.GetSize());
file.ReadBytes(bytes.data(), bytes.size());
@@ -241,8 +238,8 @@ private:
return DownloadResult::InvalidContentType;
}
- FileUtil::CreateFullPath(path);
- FileUtil::IOFile file{path, "wb"};
+ Common::FS::CreateFullPath(path);
+ Common::FS::IOFile file{path, "wb"};
if (!file.IsOpen())
return DownloadResult::GeneralFSError;
if (!file.Resize(response->body.size()))
@@ -260,7 +257,7 @@ private:
return out;
}
- std::unique_ptr<httplib::Client> client;
+ std::unique_ptr<httplib::SSLClient> client;
std::string path;
u64 title_id;
u64 build_id;
@@ -295,7 +292,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
- FileUtil::Delete(zip_path);
+ Common::FS::Delete(zip_path);
}
HandleDownloadDisplayResult(applet_manager, res);
@@ -305,7 +302,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe
progress.StartProcessingDataList();
- FileUtil::IOFile zip{zip_path, "rb"};
+ Common::FS::IOFile zip{zip_path, "rb"};
const auto size = zip.GetSize();
std::vector<u8> bytes(size);
if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
@@ -370,8 +367,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress)
std::thread([this, title, &progress] {
SynchronizeInternal(applet_manager, dir_getter, title, progress);
- })
- .detach();
+ }).detach();
return true;
}
@@ -382,8 +378,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
std::thread([this, title, name, &progress] {
SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
- })
- .detach();
+ }).detach();
return true;
}
@@ -427,7 +422,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
- FileUtil::Delete(path);
+ Common::FS::Delete(path);
}
HandleDownloadDisplayResult(applet_manager, res);
@@ -435,7 +430,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
}
}
- FileUtil::IOFile bin{path, "rb"};
+ Common::FS::IOFile bin{path, "rb"};
const auto size = bin.GetSize();
std::vector<u8> bytes(size);
if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
@@ -450,13 +445,25 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
std::map<std::string, EventStatus>& games) {
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)};
- client.set_timeout_sec(static_cast<int>(TIMEOUT_SECONDS));
+ client.set_connection_timeout(static_cast<int>(TIMEOUT_SECONDS));
+ client.set_read_timeout(static_cast<int>(TIMEOUT_SECONDS));
+ client.set_write_timeout(static_cast<int>(TIMEOUT_SECONDS));
httplib::Headers headers{
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
{std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
};
+ if (!client.is_valid()) {
+ LOG_ERROR(Service_BCAT, "Client is invalid, going offline!");
+ return StatusResult::Offline;
+ }
+
+ if (!client.is_socket_open()) {
+ LOG_ERROR(Service_BCAT, "Failed to open socket, going offline!");
+ return StatusResult::Offline;
+ }
+
const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
if (response == nullptr)
return StatusResult::Offline;
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 8bb2528c9..b31766212 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -14,6 +14,8 @@ BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module,
{0, &BCAT::CreateBcatService, "CreateBcatService"},
{1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"},
{2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"},
+ {3, nullptr, "CreateDeliveryCacheProgressService"},
+ {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 7ada67130..68deb0600 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -8,6 +8,7 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
+#include "core/core.h"
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
@@ -112,7 +113,7 @@ private:
void GetImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
- ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
+ ctx.WriteBuffer(impl);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -141,11 +142,15 @@ public:
{20301, nullptr, "RequestSuspendDeliveryTask"},
{20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
+ {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, &IBcatService::SetPassphrase, "SetPassphrase"},
+ {30101, nullptr, "Unknown"},
+ {30102, nullptr, "Unknown2"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
+ {30210, nullptr, "SetDeliveryTaskTimer"},
{30300, nullptr, "RegisterSystemApplicationDeliveryTasks"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90200, nullptr, "GetDeliveryList"},
diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp
index 1c1ecdb60..fac6b2f9c 100644
--- a/src/core/hle/service/bpc/bpc.cpp
+++ b/src/core/hle/service/bpc/bpc.cpp
@@ -23,9 +23,14 @@ public:
{5, nullptr, "GetBoardPowerControlEvent"},
{6, nullptr, "GetSleepButtonState"},
{7, nullptr, "GetPowerEvent"},
- {8, nullptr, "Unknown1"},
- {9, nullptr, "Unknown2"},
- {10, nullptr, "Unknown3"},
+ {8, nullptr, "CreateWakeupTimer"},
+ {9, nullptr, "CancelWakeupTimer"},
+ {10, nullptr, "EnableWakeupTimerOnDevice"},
+ {11, nullptr, "CreateWakeupTimerEx"},
+ {12, nullptr, "GetLastEnabledWakeupTimerType"},
+ {13, nullptr, "CleanAllWakeupTimers"},
+ {14, nullptr, "Unknown"},
+ {15, nullptr, "Unknown2"},
};
// clang-format on
@@ -38,10 +43,11 @@ public:
explicit BPC_R() : ServiceFramework{"bpc:r"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "GetExternalRtcValue"},
- {1, nullptr, "SetExternalRtcValue"},
- {2, nullptr, "ReadExternalRtcResetFlag"},
- {3, nullptr, "ClearExternalRtcResetFlag"},
+ {0, nullptr, "GetRtcTime"},
+ {1, nullptr, "SetRtcTime"},
+ {2, nullptr, "GetRtcResetDetected"},
+ {3, nullptr, "ClearRtcResetDetected"},
+ {4, nullptr, "SetUpRtcResetOnShutdown"},
};
// clang-format on
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 40a06c9fd..d4f0dd1ab 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
@@ -58,102 +59,103 @@ public:
{1, nullptr, "InitializeBluetooth"},
{2, nullptr, "EnableBluetooth"},
{3, nullptr, "DisableBluetooth"},
- {4, nullptr, "CleanupBluetooth"},
+ {4, nullptr, "FinalizeBluetooth"},
{5, nullptr, "GetAdapterProperties"},
{6, nullptr, "GetAdapterProperty"},
{7, nullptr, "SetAdapterProperty"},
- {8, nullptr, "StartDiscovery"},
- {9, nullptr, "CancelDiscovery"},
+ {8, nullptr, "StartInquiry"},
+ {9, nullptr, "StopInquiry"},
{10, nullptr, "CreateBond"},
{11, nullptr, "RemoveBond"},
{12, nullptr, "CancelBond"},
- {13, nullptr, "PinReply"},
- {14, nullptr, "SspReply"},
+ {13, nullptr, "RespondToPinRequest"},
+ {14, nullptr, "RespondToSspRequest"},
{15, nullptr, "GetEventInfo"},
{16, nullptr, "InitializeHid"},
- {17, nullptr, "HidConnect"},
- {18, nullptr, "HidDisconnect"},
- {19, nullptr, "HidSendData"},
- {20, nullptr, "HidSendData2"},
- {21, nullptr, "HidSetReport"},
- {22, nullptr, "HidGetReport"},
- {23, nullptr, "HidWakeController"},
- {24, nullptr, "HidAddPairedDevice"},
- {25, nullptr, "HidGetPairedDevice"},
- {26, nullptr, "CleanupHid"},
- {27, nullptr, "HidGetEventInfo"},
- {28, nullptr, "ExtSetTsi"},
- {29, nullptr, "ExtSetBurstMode"},
- {30, nullptr, "ExtSetZeroRetran"},
- {31, nullptr, "ExtSetMcMode"},
- {32, nullptr, "ExtStartLlrMode"},
- {33, nullptr, "ExtExitLlrMode"},
- {34, nullptr, "ExtSetRadio"},
- {35, nullptr, "ExtSetVisibility"},
- {36, nullptr, "ExtSetTbfcScan"},
+ {17, nullptr, "OpenHidConnection"},
+ {18, nullptr, "CloseHidConnection"},
+ {19, nullptr, "WriteHidData"},
+ {20, nullptr, "WriteHidData2"},
+ {21, nullptr, "SetHidReport"},
+ {22, nullptr, "GetHidReport"},
+ {23, nullptr, "TriggerConnection"},
+ {24, nullptr, "AddPairedDeviceInfo"},
+ {25, nullptr, "GetPairedDeviceInfo"},
+ {26, nullptr, "FinalizeHid"},
+ {27, nullptr, "GetHidEventInfo"},
+ {28, nullptr, "SetTsi"},
+ {29, nullptr, "EnableBurstMode"},
+ {30, nullptr, "SetZeroRetransmission"},
+ {31, nullptr, "EnableMcMode"},
+ {32, nullptr, "EnableLlrScan"},
+ {33, nullptr, "DisableLlrScan"},
+ {34, nullptr, "EnableRadio"},
+ {35, nullptr, "SetVisibility"},
+ {36, nullptr, "EnableTbfcScan"},
{37, nullptr, "RegisterHidReportEvent"},
- {38, nullptr, "HidGetReportEventInfo"},
+ {38, nullptr, "GetHidReportEventInfo"},
{39, nullptr, "GetLatestPlr"},
- {40, nullptr, "ExtGetPendingConnections"},
+ {40, nullptr, "GetPendingConnections"},
{41, nullptr, "GetChannelMap"},
- {42, nullptr, "EnableBluetoothBoostSetting"},
- {43, nullptr, "IsBluetoothBoostSettingEnabled"},
- {44, nullptr, "EnableBluetoothAfhSetting"},
- {45, nullptr, "IsBluetoothAfhSettingEnabled"},
- {46, nullptr, "InitializeBluetoothLe"},
- {47, nullptr, "EnableBluetoothLe"},
- {48, nullptr, "DisableBluetoothLe"},
- {49, nullptr, "CleanupBluetoothLe"},
- {50, nullptr, "SetLeVisibility"},
- {51, nullptr, "SetLeConnectionParameter"},
- {52, nullptr, "SetLeDefaultConnectionParameter"},
- {53, nullptr, "SetLeAdvertiseData"},
- {54, nullptr, "SetLeAdvertiseParameter"},
- {55, nullptr, "StartLeScan"},
- {56, nullptr, "StopLeScan"},
- {57, nullptr, "AddLeScanFilterCondition"},
- {58, nullptr, "DeleteLeScanFilterCondition"},
- {59, nullptr, "DeleteLeScanFilter"},
- {60, nullptr, "ClearLeScanFilters"},
- {61, nullptr, "EnableLeScanFilter"},
- {62, nullptr, "RegisterLeClient"},
- {63, nullptr, "UnregisterLeClient"},
- {64, nullptr, "UnregisterLeClientAll"},
- {65, nullptr, "LeClientConnect"},
- {66, nullptr, "LeClientCancelConnection"},
- {67, nullptr, "LeClientDisconnect"},
- {68, nullptr, "LeClientGetAttributes"},
- {69, nullptr, "LeClientDiscoverService"},
- {70, nullptr, "LeClientConfigureMtu"},
- {71, nullptr, "RegisterLeServer"},
- {72, nullptr, "UnregisterLeServer"},
- {73, nullptr, "LeServerConnect"},
- {74, nullptr, "LeServerDisconnect"},
- {75, nullptr, "CreateLeService"},
- {76, nullptr, "StartLeService"},
- {77, nullptr, "AddLeCharacteristic"},
- {78, nullptr, "AddLeDescriptor"},
- {79, nullptr, "GetLeCoreEventInfo"},
- {80, nullptr, "LeGetFirstCharacteristic"},
- {81, nullptr, "LeGetNextCharacteristic"},
- {82, nullptr, "LeGetFirstDescriptor"},
- {83, nullptr, "LeGetNextDescriptor"},
- {84, nullptr, "RegisterLeCoreDataPath"},
- {85, nullptr, "UnregisterLeCoreDataPath"},
- {86, nullptr, "RegisterLeHidDataPath"},
- {87, nullptr, "UnregisterLeHidDataPath"},
- {88, nullptr, "RegisterLeDataPath"},
- {89, nullptr, "UnregisterLeDataPath"},
- {90, nullptr, "LeClientReadCharacteristic"},
- {91, nullptr, "LeClientReadDescriptor"},
- {92, nullptr, "LeClientWriteCharacteristic"},
- {93, nullptr, "LeClientWriteDescriptor"},
- {94, nullptr, "LeClientRegisterNotification"},
- {95, nullptr, "LeClientDeregisterNotification"},
+ {42, nullptr, "EnableTxPowerBoostSetting"},
+ {43, nullptr, "IsTxPowerBoostSettingEnabled"},
+ {44, nullptr, "EnableAfhSetting"},
+ {45, nullptr, "IsAfhSettingEnabled"},
+ {46, nullptr, "InitializeBle"},
+ {47, nullptr, "EnableBle"},
+ {48, nullptr, "DisableBle"},
+ {49, nullptr, "FinalizeBle"},
+ {50, nullptr, "SetBleVisibility"},
+ {51, nullptr, "SetBleConnectionParameter"},
+ {52, nullptr, "SetBleDefaultConnectionParameter"},
+ {53, nullptr, "SetBleAdvertiseData"},
+ {54, nullptr, "SetBleAdvertiseParameter"},
+ {55, nullptr, "StartBleScan"},
+ {56, nullptr, "StopBleScan"},
+ {57, nullptr, "AddBleScanFilterCondition"},
+ {58, nullptr, "DeleteBleScanFilterCondition"},
+ {59, nullptr, "DeleteBleScanFilter"},
+ {60, nullptr, "ClearBleScanFilters"},
+ {61, nullptr, "EnableBleScanFilter"},
+ {62, nullptr, "RegisterGattClient"},
+ {63, nullptr, "UnregisterGattClient"},
+ {64, nullptr, "UnregisterAllGattClients"},
+ {65, nullptr, "ConnectGattServer"},
+ {66, nullptr, "CancelConnectGattServer"},
+ {67, nullptr, "DisconnectGattServer"},
+ {68, nullptr, "GetGattAttribute"},
+ {69, nullptr, "GetGattService"},
+ {70, nullptr, "ConfigureAttMtu"},
+ {71, nullptr, "RegisterGattServer"},
+ {72, nullptr, "UnregisterGattServer"},
+ {73, nullptr, "ConnectGattClient"},
+ {74, nullptr, "DisconnectGattClient"},
+ {75, nullptr, "AddGattService"},
+ {76, nullptr, "EnableGattService"},
+ {77, nullptr, "AddGattCharacteristic"},
+ {78, nullptr, "AddGattDescriptor"},
+ {79, nullptr, "GetBleManagedEventInfo"},
+ {80, nullptr, "GetGattFirstCharacteristic"},
+ {81, nullptr, "GetGattNextCharacteristic"},
+ {82, nullptr, "GetGattFirstDescriptor"},
+ {83, nullptr, "GetGattNextDescriptor"},
+ {84, nullptr, "RegisterGattManagedDataPath"},
+ {85, nullptr, "UnregisterGattManagedDataPath"},
+ {86, nullptr, "RegisterGattHidDataPath"},
+ {87, nullptr, "UnregisterGattHidDataPath"},
+ {88, nullptr, "RegisterGattDataPath"},
+ {89, nullptr, "UnregisterGattDataPath"},
+ {90, nullptr, "ReadGattCharacteristic"},
+ {91, nullptr, "ReadGattDescriptor"},
+ {92, nullptr, "WriteGattCharacteristic"},
+ {93, nullptr, "WriteGattDescriptor"},
+ {94, nullptr, "RegisterGattNotification"},
+ {95, nullptr, "UnregisterGattNotification"},
{96, nullptr, "GetLeHidEventInfo"},
{97, nullptr, "RegisterBleHidEvent"},
- {98, nullptr, "SetLeScanParameter"},
- {256, nullptr, "GetIsManufacturingMode"},
+ {98, nullptr, "SetBleScanParameter"},
+ {99, nullptr, "MoveToSecondaryPiconet"},
+ {256, nullptr, "IsManufacturingMode"},
{257, nullptr, "EmulateBluetoothCrash"},
{258, nullptr, "GetBleChannelMap"},
};
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 251b3c9df..c8f8ddbd5 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -5,6 +5,7 @@
#include <memory>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
@@ -132,66 +133,71 @@ public:
explicit BTM() : ServiceFramework{"btm"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"},
- {3, nullptr, "Unknown3"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "Unknown5"},
- {6, nullptr, "Unknown6"},
- {7, nullptr, "Unknown7"},
- {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"},
- {9, nullptr, "Unknown8"},
- {10, nullptr, "Unknown9"},
- {11, nullptr, "Unknown10"},
- {12, nullptr, "Unknown11"},
- {13, nullptr, "Unknown12"},
+ {0, nullptr, "GetState"},
+ {1, nullptr, "GetHostDeviceProperty"},
+ {2, nullptr, "AcquireDeviceConditionEvent"},
+ {3, nullptr, "GetDeviceCondition"},
+ {4, nullptr, "SetBurstMode"},
+ {5, nullptr, "SetSlotMode"},
+ {6, nullptr, "SetBluetoothMode"},
+ {7, nullptr, "SetWlanMode"},
+ {8, nullptr, "AcquireDeviceInfoEvent"},
+ {9, nullptr, "GetDeviceInfo"},
+ {10, nullptr, "AddDeviceInfo"},
+ {11, nullptr, "RemoveDeviceInfo"},
+ {12, nullptr, "IncreaseDeviceInfoOrder"},
+ {13, nullptr, "LlrNotify"},
{14, nullptr, "EnableRadio"},
{15, nullptr, "DisableRadio"},
- {16, nullptr, "Unknown13"},
- {17, nullptr, "Unknown14"},
- {18, nullptr, "Unknown15"},
- {19, nullptr, "Unknown16"},
- {20, nullptr, "Unknown17"},
- {21, nullptr, "Unknown18"},
- {22, nullptr, "Unknown19"},
- {23, nullptr, "Unknown20"},
- {24, nullptr, "Unknown21"},
- {25, nullptr, "Unknown22"},
- {26, nullptr, "Unknown23"},
- {27, nullptr, "Unknown24"},
- {28, nullptr, "Unknown25"},
- {29, nullptr, "Unknown26"},
- {30, nullptr, "Unknown27"},
- {31, nullptr, "Unknown28"},
- {32, nullptr, "Unknown29"},
- {33, nullptr, "Unknown30"},
- {34, nullptr, "Unknown31"},
- {35, nullptr, "Unknown32"},
- {36, nullptr, "Unknown33"},
- {37, nullptr, "Unknown34"},
- {38, nullptr, "Unknown35"},
- {39, nullptr, "Unknown36"},
- {40, nullptr, "Unknown37"},
- {41, nullptr, "Unknown38"},
- {42, nullptr, "Unknown39"},
- {43, nullptr, "Unknown40"},
- {44, nullptr, "Unknown41"},
- {45, nullptr, "Unknown42"},
- {46, nullptr, "Unknown43"},
- {47, nullptr, "Unknown44"},
- {48, nullptr, "Unknown45"},
- {49, nullptr, "Unknown46"},
- {50, nullptr, "Unknown47"},
- {51, nullptr, "Unknown48"},
- {52, nullptr, "Unknown49"},
- {53, nullptr, "Unknown50"},
- {54, nullptr, "Unknown51"},
- {55, nullptr, "Unknown52"},
- {56, nullptr, "Unknown53"},
- {57, nullptr, "Unknown54"},
- {58, nullptr, "Unknown55"},
- {59, nullptr, "Unknown56"},
+ {16, nullptr, "HidDisconnect"},
+ {17, nullptr, "HidSetRetransmissionMode"},
+ {18, nullptr, "AcquireAwakeReqEvent"},
+ {19, nullptr, "AcquireLlrStateEvent"},
+ {20, nullptr, "IsLlrStarted"},
+ {21, nullptr, "EnableSlotSaving"},
+ {22, nullptr, "ProtectDeviceInfo"},
+ {23, nullptr, "AcquireBleScanEvent"},
+ {24, nullptr, "GetBleScanParameterGeneral"},
+ {25, nullptr, "GetBleScanParameterSmartDevice"},
+ {26, nullptr, "StartBleScanForGeneral"},
+ {27, nullptr, "StopBleScanForGeneral"},
+ {28, nullptr, "GetBleScanResultsForGeneral"},
+ {29, nullptr, "StartBleScanForPairedDevice"},
+ {30, nullptr, "StopBleScanForPairedDevice"},
+ {31, nullptr, "StartBleScanForSmartDevice"},
+ {32, nullptr, "StopBleScanForSmartDevice"},
+ {33, nullptr, "GetBleScanResultsForSmartDevice"},
+ {34, nullptr, "AcquireBleConnectionEvent"},
+ {35, nullptr, "BleConnect"},
+ {36, nullptr, "BleOverrideConnection"},
+ {37, nullptr, "BleDisconnect"},
+ {38, nullptr, "BleGetConnectionState"},
+ {39, nullptr, "BleGetGattClientConditionList"},
+ {40, nullptr, "AcquireBlePairingEvent"},
+ {41, nullptr, "BlePairDevice"},
+ {42, nullptr, "BleUnpairDeviceOnBoth"},
+ {43, nullptr, "BleUnpairDevice"},
+ {44, nullptr, "BleGetPairedAddresses"},
+ {45, nullptr, "AcquireBleServiceDiscoveryEvent"},
+ {46, nullptr, "GetGattServices"},
+ {47, nullptr, "GetGattService"},
+ {48, nullptr, "GetGattIncludedServices"},
+ {49, nullptr, "GetBelongingService"},
+ {50, nullptr, "GetGattCharacteristics"},
+ {51, nullptr, "GetGattDescriptors"},
+ {52, nullptr, "AcquireBleMtuConfigEvent"},
+ {53, nullptr, "ConfigureBleMtu"},
+ {54, nullptr, "GetBleMtu"},
+ {55, nullptr, "RegisterBleGattDataPath"},
+ {56, nullptr, "UnregisterBleGattDataPath"},
+ {57, nullptr, "RegisterAppletResourceUserId"},
+ {58, nullptr, "UnregisterAppletResourceUserId"},
+ {59, nullptr, "SetAppletResourceUserId"},
+ {60, nullptr, "Unknown60"},
+ {61, nullptr, "Unknown61"},
+ {62, nullptr, "Unknown62"},
+ {63, nullptr, "Unknown63"},
+ {64, nullptr, "Unknown64"},
};
// clang-format on
@@ -204,19 +210,19 @@ public:
explicit BTM_DBG() : ServiceFramework{"btm:dbg"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "RegisterSystemEventForDiscovery"},
- {1, nullptr, "Unknown1"},
- {2, nullptr, "Unknown2"},
- {3, nullptr, "Unknown3"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "Unknown5"},
- {6, nullptr, "Unknown6"},
- {7, nullptr, "Unknown7"},
- {8, nullptr, "Unknown8"},
- {9, nullptr, "Unknown9"},
- {10, nullptr, "Unknown10"},
- {11, nullptr, "Unknown11"},
- {12, nullptr, "Unknown11"},
+ {0, nullptr, "AcquireDiscoveryEvent"},
+ {1, nullptr, "StartDiscovery"},
+ {2, nullptr, "CancelDiscovery"},
+ {3, nullptr, "GetDeviceProperty"},
+ {4, nullptr, "CreateBond"},
+ {5, nullptr, "CancelBond"},
+ {6, nullptr, "SetTsiMode"},
+ {7, nullptr, "GeneralTest"},
+ {8, nullptr, "HidConnect"},
+ {9, nullptr, "GeneralGet"},
+ {10, nullptr, "GetGattClientDisconnectionReason"},
+ {11, nullptr, "GetBleConnectionParameter"},
+ {12, nullptr, "GetBleConnectionParameterRequest"},
};
// clang-format on
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
index 26c8a7081..ba5749b84 100644
--- a/src/core/hle/service/caps/caps.cpp
+++ b/src/core/hle/service/caps/caps.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
index fc70a4c27..b8c67b6e2 100644
--- a/src/core/hle/service/caps/caps.h
+++ b/src/core/hle/service/caps/caps.h
@@ -1,4 +1,4 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -12,73 +12,79 @@ class ServiceManager;
namespace Service::Capture {
-enum AlbumImageOrientation {
+enum class AlbumImageOrientation {
Orientation0 = 0,
Orientation1 = 1,
Orientation2 = 2,
Orientation3 = 3,
};
-enum AlbumReportOption {
+enum class AlbumReportOption {
Disable = 0,
Enable = 1,
};
-enum ContentType : u8 {
+enum class ContentType : u8 {
Screenshot = 0,
Movie = 1,
ExtraMovie = 3,
};
-enum AlbumStorage : u8 {
+enum class AlbumStorage : u8 {
NAND = 0,
SD = 1,
};
struct AlbumFileDateTime {
- u16 year;
- u8 month;
- u8 day;
- u8 hour;
- u8 minute;
- u8 second;
- u8 uid;
+ s16 year{};
+ s8 month{};
+ s8 day{};
+ s8 hour{};
+ s8 minute{};
+ s8 second{};
+ s8 uid{};
};
+static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size.");
struct AlbumEntry {
- u64 size;
- u64 application_id;
- AlbumFileDateTime datetime;
- AlbumStorage storage;
- ContentType content;
- u8 padding[6];
+ u64 size{};
+ u64 application_id{};
+ AlbumFileDateTime datetime{};
+ AlbumStorage storage{};
+ ContentType content{};
+ INSERT_PADDING_BYTES(6);
};
+static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size.");
struct AlbumFileEntry {
- u64 size;
- u64 hash;
- AlbumFileDateTime datetime;
- AlbumStorage storage;
- ContentType content;
- u8 padding[5];
- u8 unknown;
+ u64 size{}; // Size of the entry
+ u64 hash{}; // AES256 with hardcoded key over AlbumEntry
+ AlbumFileDateTime datetime{};
+ AlbumStorage storage{};
+ ContentType content{};
+ INSERT_PADDING_BYTES(5);
+ u8 unknown{1}; // Set to 1 on official SW
};
+static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size.");
struct ApplicationAlbumEntry {
- u64 size;
- u64 hash;
- AlbumFileDateTime datetime;
- AlbumStorage storage;
- ContentType content;
- u8 padding[5];
- u8 unknown;
+ u64 size{}; // Size of the entry
+ u64 hash{}; // AES256 with hardcoded key over AlbumEntry
+ AlbumFileDateTime datetime{};
+ AlbumStorage storage{};
+ ContentType content{};
+ INSERT_PADDING_BYTES(5);
+ u8 unknown{1}; // Set to 1 on official SW
};
+static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size.");
struct ApplicationAlbumFileEntry {
- ApplicationAlbumEntry entry;
- AlbumFileDateTime datetime;
- u64 unknown;
+ ApplicationAlbumEntry entry{};
+ AlbumFileDateTime datetime{};
+ u64 unknown{};
};
+static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
+ "ApplicationAlbumFileEntry has incorrect size.");
/// Registers all Capture services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 88a3fdc05..a0a3b2ae3 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h
index 8de832491..cb93aad5b 100644
--- a/src/core/hle/service/caps/caps_a.h
+++ b/src/core/hle/service/caps/caps_a.h
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp
index ea6452ffa..a0ee116fa 100644
--- a/src/core/hle/service/caps/caps_c.cpp
+++ b/src/core/hle/service/caps/caps_c.cpp
@@ -1,7 +1,9 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/caps/caps_c.h"
namespace Service::Capture {
@@ -47,7 +49,7 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") {
static const FunctionInfo functions[] = {
{1, nullptr, "CaptureRawImage"},
{2, nullptr, "CaptureRawImageWithTimeout"},
- {33, nullptr, "Unknown33"},
+ {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"},
{1001, nullptr, "RequestTakingScreenShot"},
{1002, nullptr, "RequestTakingScreenShotWithTimeout"},
{1011, nullptr, "NotifyTakingScreenShotRefused"},
@@ -72,4 +74,16 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") {
CAPS_C::~CAPS_C() = default;
+void CAPS_C::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto library_version{rp.Pop<u64>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
+ library_version, applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h
index d07cdb441..b110301d4 100644
--- a/src/core/hle/service/caps/caps_c.h
+++ b/src/core/hle/service/caps/caps_c.h
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -16,6 +16,9 @@ class CAPS_C final : public ServiceFramework<CAPS_C> {
public:
explicit CAPS_C();
~CAPS_C() override;
+
+private:
+ void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp
index d01a8a58e..822ee96c8 100644
--- a/src/core/hle/service/caps/caps_sc.cpp
+++ b/src/core/hle/service/caps/caps_sc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h
index 9ba372f7a..ac3e929ca 100644
--- a/src/core/hle/service/caps/caps_sc.h
+++ b/src/core/hle/service/caps/caps_sc.h
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp
index eaa3a7494..24dc716e7 100644
--- a/src/core/hle/service/caps/caps_ss.cpp
+++ b/src/core/hle/service/caps/caps_ss.cpp
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h
index e258a6925..450686e4f 100644
--- a/src/core/hle/service/caps/caps_ss.h
+++ b/src/core/hle/service/caps/caps_ss.h
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp
index 2b4c2d808..e386470f7 100644
--- a/src/core/hle/service/caps/caps_su.cpp
+++ b/src/core/hle/service/caps/caps_su.cpp
@@ -1,7 +1,9 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/caps/caps_su.h"
namespace Service::Capture {
@@ -9,8 +11,11 @@ namespace Service::Capture {
CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") {
// clang-format off
static const FunctionInfo functions[] = {
+ {32, &CAPS_SU::SetShimLibraryVersion, "SetShimLibraryVersion"},
{201, nullptr, "SaveScreenShot"},
{203, nullptr, "SaveScreenShotEx0"},
+ {205, nullptr, "SaveScreenShotEx1"},
+ {210, nullptr, "SaveScreenShotEx2"},
};
// clang-format on
@@ -19,4 +24,16 @@ CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") {
CAPS_SU::~CAPS_SU() = default;
+void CAPS_SU::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto library_version{rp.Pop<u64>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
+ library_version, applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h
index cb11f7c9a..62c9603a9 100644
--- a/src/core/hle/service/caps/caps_su.h
+++ b/src/core/hle/service/caps/caps_su.h
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -16,6 +16,9 @@ class CAPS_SU final : public ServiceFramework<CAPS_SU> {
public:
explicit CAPS_SU();
~CAPS_SU() override;
+
+private:
+ void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index 78bab6ed8..f9479bdb3 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -31,8 +31,7 @@ public:
CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
// clang-format off
static const FunctionInfo functions[] = {
- {31, nullptr, "GetShimLibraryVersion"},
- {32, nullptr, "SetShimLibraryVersion"},
+ {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"},
{102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"},
{103, nullptr, "DeleteAlbumContentsFileForApplication"},
{104, nullptr, "GetAlbumContentsFileSizeForApplication"},
@@ -42,7 +41,7 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
{130, nullptr, "PrecheckToCreateContentsForApplication"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
- {142, nullptr, "GetAlbumFileList3AaeAruid"},
+ {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
};
@@ -53,24 +52,49 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
CAPS_U::~CAPS_U() = default;
+void CAPS_U::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto library_version{rp.Pop<u64>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
+ library_version, applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) {
// Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an
// u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total
// output entries (which is copied to a s32 by official SW).
IPC::RequestParser rp{ctx};
- [[maybe_unused]] const auto application_album_file_entries = rp.PopRaw<std::array<u8, 0x30>>();
- const auto pid = rp.Pop<s32>();
- const auto content_type = rp.PopRaw<ContentType>();
- [[maybe_unused]] const auto start_datetime = rp.PopRaw<AlbumFileDateTime>();
- [[maybe_unused]] const auto end_datetime = rp.PopRaw<AlbumFileDateTime>();
- const auto applet_resource_user_id = rp.Pop<u64>();
- LOG_WARNING(Service_Capture,
- "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid,
- content_type, applet_resource_user_id);
-
- IPC::ResponseBuilder rb{ctx, 3};
+ const auto pid{rp.Pop<s32>()};
+ const auto content_type{rp.PopEnum<ContentType>()};
+ const auto start_posix_time{rp.Pop<s64>()};
+ const auto end_posix_time{rp.Pop<s64>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ // TODO: Update this when we implement the album.
+ // Currently we do not have a method of accessing album entries, set this to 0 for now.
+ constexpr u32 total_entries_1{};
+ constexpr u32 total_entries_2{};
+
+ LOG_WARNING(
+ Service_Capture,
+ "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
+ "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
+ pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
+ total_entries_1, total_entries_2);
+
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<s32>(0);
+ rb.Push(total_entries_1);
+ rb.Push(total_entries_2);
+}
+
+void CAPS_U::GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx) {
+ GetAlbumContentsFileListForApplication(ctx);
}
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index e6e0716ff..4b80f3156 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -1,4 +1,4 @@
-// Copyright 2020 yuzu emulator team
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -18,7 +18,9 @@ public:
~CAPS_U() override;
private:
+ void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
+ void GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index df00ae625..c2737a365 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -4,6 +4,7 @@
#include "core/crypto/key_manager.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/es/es.h"
#include "core/hle/service/service.h"
namespace Service::ES {
@@ -26,8 +27,8 @@ public:
{8, &ETicket::GetTitleKey, "GetTitleKey"},
{9, &ETicket::CountCommonTicket, "CountCommonTicket"},
{10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"},
- {11, &ETicket::ListCommonTicket, "ListCommonTicket"},
- {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"},
+ {11, &ETicket::ListCommonTicketRightsIds, "ListCommonTicketRightsIds"},
+ {12, &ETicket::ListPersonalizedTicketRightsIds, "ListPersonalizedTicketRightsIds"},
{13, nullptr, "ListMissingPersonalizedTicket"},
{14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"},
{15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"},
@@ -54,7 +55,46 @@ public:
{36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
{37, nullptr, "OwnTicket2"},
{38, nullptr, "OwnTicket3"},
+ {501, nullptr, "Unknown501"},
+ {502, nullptr, "Unknown502"},
{503, nullptr, "GetTitleKey"},
+ {504, nullptr, "Unknown504"},
+ {508, nullptr, "Unknown508"},
+ {509, nullptr, "Unknown509"},
+ {510, nullptr, "Unknown510"},
+ {511, nullptr, "Unknown511"},
+ {1001, nullptr, "Unknown1001"},
+ {1002, nullptr, "Unknown1001"},
+ {1003, nullptr, "Unknown1003"},
+ {1004, nullptr, "Unknown1004"},
+ {1005, nullptr, "Unknown1005"},
+ {1006, nullptr, "Unknown1006"},
+ {1007, nullptr, "Unknown1007"},
+ {1009, nullptr, "Unknown1009"},
+ {1010, nullptr, "Unknown1010"},
+ {1011, nullptr, "Unknown1011"},
+ {1012, nullptr, "Unknown1012"},
+ {1013, nullptr, "Unknown1013"},
+ {1014, nullptr, "Unknown1014"},
+ {1015, nullptr, "Unknown1015"},
+ {1016, nullptr, "Unknown1016"},
+ {1017, nullptr, "Unknown1017"},
+ {1018, nullptr, "Unknown1018"},
+ {1019, nullptr, "Unknown1019"},
+ {1020, nullptr, "Unknown1020"},
+ {1021, nullptr, "Unknown1021"},
+ {1501, nullptr, "Unknown1501"},
+ {1502, nullptr, "Unknown1502"},
+ {1503, nullptr, "Unknown1503"},
+ {1504, nullptr, "Unknown1504"},
+ {1505, nullptr, "Unknown1505"},
+ {2000, nullptr, "Unknown2000"},
+ {2001, nullptr, "Unknown2001"},
+ {2100, nullptr, "Unknown2100"},
+ {2501, nullptr, "Unknown2501"},
+ {2502, nullptr, "Unknown2502"},
+ {3001, nullptr, "Unknown3001"},
+ {3002, nullptr, "Unknown3002"},
};
// clang-format on
RegisterHandlers(functions);
@@ -76,7 +116,6 @@ private:
}
void ImportTicket(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
const auto ticket = ctx.ReadBuffer();
const auto cert = ctx.ReadBuffer(1);
@@ -121,7 +160,7 @@ private:
return;
}
- ctx.WriteBuffer(key.data(), key.size());
+ ctx.WriteBuffer(key);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -147,7 +186,7 @@ private:
rb.Push<u32>(count);
}
- void ListCommonTicket(Kernel::HLERequestContext& ctx) {
+ void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) {
u32 out_entries;
if (keys.GetCommonTickets().empty())
out_entries = 0;
@@ -170,7 +209,7 @@ private:
rb.Push<u32>(out_entries);
}
- void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) {
+ void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) {
u32 out_entries;
if (keys.GetPersonalizedTickets().empty())
out_entries = 0;
@@ -263,7 +302,7 @@ private:
rb.Push<u64>(write_size);
}
- Core::Crypto::KeyManager keys;
+ Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
};
void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp
index 2df30acee..0d6d244f4 100644
--- a/src/core/hle/service/eupld/eupld.cpp
+++ b/src/core/hle/service/eupld/eupld.cpp
@@ -19,6 +19,7 @@ public:
{1, nullptr, "ImportCrt"},
{2, nullptr, "ImportPki"},
{3, nullptr, "SetAutoUpload"},
+ {4, nullptr, "GetAutoUpload"},
};
// clang-format on
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 102017d73..2e53cae5b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -36,7 +36,7 @@ constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name_) {
- std::string dir_name(FileUtil::SanitizePath(dir_name_));
+ std::string dir_name(Common::FS::SanitizePath(dir_name_));
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
return base;
@@ -53,9 +53,13 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
}
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- auto file = dir->CreateFile(FileUtil::GetFilename(path));
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ // dir can be nullptr if path contains subdirectories, create those prior to creating the file.
+ if (dir == nullptr) {
+ dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path));
+ }
+ auto file = dir->CreateFile(Common::FS::GetFilename(path));
if (file == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
@@ -68,17 +72,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
}
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
+ std::string path(Common::FS::SanitizePath(path_));
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
- if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
+ if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -87,11 +91,12 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
}
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) {
dir = backing;
- auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
+ }
+ auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
@@ -100,9 +105,9 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -110,9 +115,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+ if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -120,10 +125,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
}
ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
- const std::string sanitized_path(FileUtil::SanitizePath(path));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path));
+ const std::string sanitized_path(Common::FS::SanitizePath(path));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
- if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) {
+ if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -133,14 +138,14 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
const std::string& dest_path_) const {
- std::string src_path(FileUtil::SanitizePath(src_path_));
- std::string dest_path(FileUtil::SanitizePath(dest_path_));
+ std::string src_path(Common::FS::SanitizePath(src_path_));
+ std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = backing->GetFileRelative(src_path);
- if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
+ if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
- if (!src->Rename(FileUtil::GetFilename(dest_path))) {
+ if (!src->Rename(Common::FS::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -158,7 +163,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
"Could not write all of the bytes but everything else has succeded.");
- if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) {
+ if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -168,14 +173,14 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
const std::string& dest_path_) const {
- std::string src_path(FileUtil::SanitizePath(src_path_));
- std::string dest_path(FileUtil::SanitizePath(dest_path_));
+ std::string src_path(Common::FS::SanitizePath(src_path_));
+ std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = GetDirectoryRelativeWrapped(backing, src_path);
- if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
+ if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
- if (!src->Rename(FileUtil::GetFilename(dest_path))) {
+ if (!src->Rename(Common::FS::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return RESULT_UNKNOWN;
}
@@ -194,7 +199,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
FileSys::Mode mode) const {
- const std::string path(FileUtil::SanitizePath(path_));
+ const std::string path(Common::FS::SanitizePath(path_));
std::string_view npath = path;
while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
npath.remove_prefix(1);
@@ -214,7 +219,7 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
}
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
- std::string path(FileUtil::SanitizePath(path_));
+ std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
@@ -225,11 +230,11 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
const std::string& path_) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
+ std::string path(Common::FS::SanitizePath(path_));
+ auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
- auto filename = FileUtil::GetFilename(path);
+ auto filename = Common::FS::GetFilename(path);
// TODO(Subv): Some games use the '/' path, find out what this means.
if (filename.empty())
return MakeResult(FileSys::EntryType::Directory);
@@ -307,7 +312,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
}
ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), save_struct.DebugInfo());
@@ -319,15 +324,15 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
}
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
- static_cast<u8>(space), descriptor.DebugInfo());
+ static_cast<u8>(space), attribute.DebugInfo());
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
- return save_data_factory->Open(space, descriptor);
+ return save_data_factory->Open(space, attribute);
}
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
@@ -375,7 +380,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
- auto part = bis_factory->OpenPartitionStorage(id);
+ auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
if (part == nullptr) {
return FileSys::ERROR_INVALID_ARGUMENT;
}
@@ -450,8 +455,11 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
- FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
- auto [nacp_unique, discard] = pm.GetControlMetadata();
+ const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ const auto metadata = pm.GetControlMetadata();
+ const auto& nacp_unique = metadata.first;
if (nacp_unique != nullptr) {
new_size = {nacp_unique->GetDefaultNormalSaveSize(),
@@ -690,13 +698,13 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
sdmc_factory = nullptr;
}
- auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
+ auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
FileSys::Mode::ReadWrite);
- auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
+ auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir),
FileSys::Mode::ReadWrite);
- auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
+ auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
FileSys::Mode::ReadWrite);
- auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
+ auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
FileSys::Mode::ReadWrite);
if (bis_factory == nullptr) {
@@ -722,7 +730,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
void InstallInterfaces(Core::System& system) {
std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
- std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
+ std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetContentProvider(),
+ system.GetReporter())
->InstallAsService(system.ServiceManager());
}
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 1b0a6a949..6dbbf0b2b 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -31,7 +31,7 @@ enum class SaveDataSpaceId : u8;
enum class SaveDataType : u8;
enum class StorageId : u8;
-struct SaveDataDescriptor;
+struct SaveDataAttribute;
struct SaveDataSize;
} // namespace FileSys
@@ -69,9 +69,9 @@ public:
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const;
ResultVal<FileSys::VirtualDir> CreateSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
ResultVal<FileSys::VirtualDir> OpenSDMC() const;
ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index e6811d5b5..031c6dbf6 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -316,8 +316,8 @@ public:
{8, &IFileSystem::OpenFile, "OpenFile"},
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
{10, &IFileSystem::Commit, "Commit"},
- {11, nullptr, "GetFreeSpaceSize"},
- {12, nullptr, "GetTotalSpaceSize"},
+ {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
+ {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
{14, nullptr, "GetFileTimeStampRaw"},
{15, nullptr, "QueryEntry"},
@@ -575,6 +575,7 @@ private:
0,
user_id->GetSize(),
{},
+ {},
});
continue;
@@ -595,6 +596,7 @@ private:
stoull_be(title_id->GetName()),
title_id->GetSize(),
{},
+ {},
});
}
}
@@ -619,6 +621,7 @@ private:
stoull_be(title_id->GetName()),
title_id->GetSize(),
{},
+ {},
});
}
}
@@ -647,8 +650,10 @@ private:
u64 next_entry_index = 0;
};
-FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
- : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
+FSP_SRV::FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
+ const Core::Reporter& reporter_)
+ : ServiceFramework("fsp-srv"), fsc(fsc_), content_provider{content_provider_},
+ reporter(reporter_) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenFileSystem"},
@@ -693,13 +698,15 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
{67, nullptr, "FindSaveDataWithFilter"},
{68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
{69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
- {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
+ {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
+ {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
{80, nullptr, "OpenSaveDataMetaFile"},
{81, nullptr, "OpenSaveDataTransferManager"},
{82, nullptr, "OpenSaveDataTransferManagerVersion2"},
{83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
{84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
{85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"},
+ {86, nullptr, "OpenSaveDataMover"},
{100, nullptr, "OpenImageDirectoryFileSystem"},
{110, nullptr, "OpenContentStorageFileSystem"},
{120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
@@ -759,9 +766,11 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
{1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"},
{1012, nullptr, "GetFsStackUsage"},
{1013, nullptr, "UnsetSaveDataRootPath"},
+ {1014, nullptr, "OutputMultiProgramTagAccessLog"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
- {1200, nullptr, "OpenMultiCommitManager"},
+ {1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},
+ {1300, nullptr, "OpenBisWiper"},
};
// clang-format on
RegisterHandlers(functions);
@@ -805,7 +814,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
+ auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
@@ -819,31 +828,40 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
}
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_FS, "called.");
+ IPC::RequestParser rp{ctx};
struct Parameters {
- FileSys::SaveDataSpaceId save_data_space_id;
- FileSys::SaveDataDescriptor descriptor;
+ FileSys::SaveDataSpaceId space_id;
+ FileSys::SaveDataAttribute attribute;
};
- IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
- auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
+ LOG_INFO(Service_FS, "called.");
+
+ auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute);
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
return;
}
- FileSys::StorageId id;
- if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
+ FileSys::StorageId id{};
+ switch (parameters.space_id) {
+ case FileSys::SaveDataSpaceId::NandUser:
id = FileSys::StorageId::NandUser;
- } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
- parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
+ break;
+ case FileSys::SaveDataSpaceId::SdCardSystem:
+ case FileSys::SaveDataSpaceId::SdCardUser:
id = FileSys::StorageId::SdCard;
- } else {
+ break;
+ case FileSys::SaveDataSpaceId::NandSystem:
id = FileSys::StorageId::NandSystem;
+ break;
+ case FileSys::SaveDataSpaceId::TemporaryStorage:
+ case FileSys::SaveDataSpaceId::ProperSystem:
+ case FileSys::SaveDataSpaceId::SafeMode:
+ UNREACHABLE();
}
auto filesystem =
@@ -869,22 +887,38 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
}
-void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- log_mode = rp.PopEnum<LogMode>();
-
- LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
+void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_FS, "called");
+void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
+ Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ struct Parameters {
+ FileSys::SaveDataSpaceId space_id;
+ FileSys::SaveDataAttribute attribute;
+ };
+
+ const auto parameters = rp.PopRaw<Parameters>();
+ // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
+ constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);
+
+ LOG_WARNING(Service_FS,
+ "(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n"
+ "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
+ "attribute.type={}, attribute.rank={}, attribute.index={}",
+ flags, static_cast<u32>(parameters.space_id), parameters.attribute.title_id,
+ parameters.attribute.user_id[1], parameters.attribute.user_id[0],
+ parameters.attribute.save_id, static_cast<u32>(parameters.attribute.type),
+ static_cast<u32>(parameters.attribute.rank), parameters.attribute.index);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.PushEnum(log_mode);
+ rb.Push(flags);
}
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
@@ -936,7 +970,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
return;
}
- FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, fsc, content_provider};
auto storage = std::make_shared<IStorage>(
pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
@@ -959,6 +993,24 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
}
+void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ log_mode = rp.PopEnum<LogMode>();
+
+ LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(log_mode);
+}
+
void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
const auto raw = ctx.ReadBuffer();
auto log = Common::StringFromFixedZeroTerminatedBuffer(
@@ -981,4 +1033,40 @@ void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) {
rb.Push(access_log_program_index);
}
+class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
+public:
+ explicit IMultiCommitManager() : ServiceFramework("IMultiCommitManager") {
+ static const FunctionInfo functions[] = {
+ {1, &IMultiCommitManager::Add, "Add"},
+ {2, &IMultiCommitManager::Commit, "Commit"},
+ };
+ RegisterHandlers(functions);
+ }
+
+private:
+ FileSys::VirtualFile backend;
+
+ void Add(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Commit(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+};
+
+void FSP_SRV::OpenMultiCommitManager(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>());
+}
+
} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index d52b55999..6c7239e6a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -12,8 +12,9 @@ class Reporter;
}
namespace FileSys {
+class ContentProvider;
class FileSystemBackend;
-}
+} // namespace FileSys
namespace Service::FileSystem {
@@ -32,7 +33,8 @@ enum class LogMode : u32 {
class FSP_SRV final : public ServiceFramework<FSP_SRV> {
public:
- explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
+ explicit FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
+ const Core::Reporter& reporter_);
~FSP_SRV() override;
private:
@@ -43,15 +45,19 @@ private:
void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
- void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
- void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
+ void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx);
+ void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
+ void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
+ void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
+ void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
FileSystemController& fsc;
+ const FileSys::ContentProvider& content_provider;
FileSys::VirtualFile romfs;
u64 current_process_id = 0;
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 7938b4b80..ebb323da2 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -5,6 +5,7 @@
#include <queue>
#include "common/logging/log.h"
#include "common/uuid.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
@@ -25,9 +26,13 @@ public:
{10101, &IFriendService::GetFriendList, "GetFriendList"},
{10102, nullptr, "UpdateFriendInfo"},
{10110, nullptr, "GetFriendProfileImage"},
+ {10120, nullptr, "Unknown10120"},
+ {10121, nullptr, "Unknown10121"},
{10200, nullptr, "SendFriendRequestForApplication"},
{10211, nullptr, "AddFacedFriendRequestForApplication"},
{10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
+ {10420, nullptr, "Unknown10420"},
+ {10421, nullptr, "Unknown10421"},
{10500, nullptr, "GetProfileList"},
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
{10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
@@ -96,6 +101,9 @@ public:
{30830, nullptr, "ClearPlayLog"},
{30900, nullptr, "SendFriendInvitation"},
{30910, nullptr, "ReadFriendInvitation"},
+ {30911, nullptr, "ReadAllFriendInvitations"},
+ {40100, nullptr, "Unknown40100"},
+ {40400, nullptr, "Unknown40400"},
{49900, nullptr, "DeleteNetworkServiceAccountCache"},
};
// clang-format on
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index b591ce31b..c6252ff89 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -5,6 +5,7 @@
#include <memory>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h
index c2874c585..f6647f724 100644
--- a/src/core/hle/service/glue/errors.h
+++ b/src/core/hle/service/glue/errors.h
@@ -8,9 +8,9 @@
namespace Service::Glue {
-constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 0x1E};
-constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 0x1F};
-constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 0x2A};
-constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 0x66};
+constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 30};
+constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31};
+constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 42};
+constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 102};
} // namespace Service::Glue
diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp
index 24910ac6c..401e0b208 100644
--- a/src/core/hle/service/grc/grc.cpp
+++ b/src/core/hle/service/grc/grc.cpp
@@ -17,6 +17,9 @@ public:
static const FunctionInfo functions[] = {
{1, nullptr, "OpenContinuousRecorder"},
{2, nullptr, "OpenGameMovieTrimmer"},
+ {3, nullptr, "OpenOffscreenRecorder"},
+ {101, nullptr, "CreateMovieMaker"},
+ {9903, nullptr, "SetOffscreenRecordingMarker"}
};
// clang-format on
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 8bc69c372..f47a9e61c 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -31,6 +31,10 @@ public:
virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) = 0;
+ // When the controller is requesting a motion update for the shared memory
+ virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+ std::size_t size) {}
+
// Called when input devices should be loaded
virtual void OnLoadInputDevices() = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 1f2131ec8..ad251ed4a 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -23,7 +23,7 @@ void Controller_DebugPad::OnRelease() {}
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetTicks();
+ shared_memory.header.timestamp = core_timing.GetCPUTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -39,33 +39,36 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing,
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- cur_entry.attribute.connected.Assign(1);
- auto& pad = cur_entry.pad_state;
- using namespace Settings::NativeButton;
- pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
- pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
- pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
- pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
- pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
- pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
- pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
- pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
- pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
+ if (Settings::values.debug_pad_enabled) {
+ cur_entry.attribute.connected.Assign(1);
+ auto& pad = cur_entry.pad_state;
- const auto [stick_l_x_f, stick_l_y_f] =
- analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
- const auto [stick_r_x_f, stick_r_y_f] =
- analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
- cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
- cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
- cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
- cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+ using namespace Settings::NativeButton;
+ pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
+ pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
+
+ const auto [stick_l_x_f, stick_l_y_f] =
+ analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+ const auto [stick_r_x_f, stick_r_y_f] =
+ analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
+ cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+ cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+ cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+ cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+ }
std::memcpy(data, &shared_memory, sizeof(SharedMemory));
}
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 6e990dd00..b7b7bfeae 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -19,7 +19,7 @@ void Controller_Gesture::OnRelease() {}
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetTicks();
+ shared_memory.header.timestamp = core_timing.GetCPUTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 358cb9329..59b694cd4 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -21,7 +21,7 @@ void Controller_Keyboard::OnRelease() {}
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetTicks();
+ shared_memory.header.timestamp = core_timing.GetCPUTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -38,16 +38,18 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing,
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
- for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
- cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
+ cur_entry.key.fill(0);
+ cur_entry.modifier = 0;
+ if (Settings::values.keyboard_enabled) {
+ for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
+ auto& entry = cur_entry.key[i / KEYS_PER_BYTE];
+ entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
}
- }
- for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
- cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
+ for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
+ cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
+ }
}
-
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 93d88ea50..ac40989c5 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -19,7 +19,7 @@ void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetTicks();
+ shared_memory.header.timestamp = core_timing.GetCPUTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index c1e32b28c..e2539ded8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
+constexpr std::size_t HANDHELD_INDEX = 8;
constexpr std::array<u32, 10> npad_id_list{
0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
-static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
+Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
+ Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
- return Controller_NPad::NPadControllerType::ProController;
- case Settings::ControllerType::DualJoycon:
- return Controller_NPad::NPadControllerType::JoyDual;
+ return NPadControllerType::ProController;
+ case Settings::ControllerType::DualJoyconDetached:
+ return NPadControllerType::JoyDual;
case Settings::ControllerType::LeftJoycon:
- return Controller_NPad::NPadControllerType::JoyLeft;
+ return NPadControllerType::JoyLeft;
case Settings::ControllerType::RightJoycon:
- return Controller_NPad::NPadControllerType::JoyRight;
+ return NPadControllerType::JoyRight;
+ case Settings::ControllerType::Handheld:
+ return NPadControllerType::Handheld;
default:
UNREACHABLE();
- return Controller_NPad::NPadControllerType::JoyDual;
+ return NPadControllerType::ProController;
+ }
+}
+
+Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
+ Controller_NPad::NPadControllerType type) {
+ switch (type) {
+ case NPadControllerType::ProController:
+ return Settings::ControllerType::ProController;
+ case NPadControllerType::JoyDual:
+ return Settings::ControllerType::DualJoyconDetached;
+ case NPadControllerType::JoyLeft:
+ return Settings::ControllerType::LeftJoycon;
+ case NPadControllerType::JoyRight:
+ return Settings::ControllerType::RightJoycon;
+ case NPadControllerType::Handheld:
+ return Settings::ControllerType::Handheld;
+ default:
+ UNREACHABLE();
+ return Settings::ControllerType::ProController;
}
}
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
case 6:
case 7:
return npad_id;
- case 8:
+ case HANDHELD_INDEX:
case NPAD_HANDHELD:
- return 8;
+ return HANDHELD_INDEX;
case 9:
case NPAD_UNKNOWN:
return 9;
@@ -83,37 +106,51 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
case 6:
case 7:
return static_cast<u32>(index);
- case 8:
+ case HANDHELD_INDEX:
return NPAD_HANDHELD;
case 9:
return NPAD_UNKNOWN;
default:
UNIMPLEMENTED_MSG("Unknown npad index {}", index);
return 0;
- };
+ }
}
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
-Controller_NPad::~Controller_NPad() = default;
-void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
+Controller_NPad::~Controller_NPad() {
+ OnRelease();
+}
+
+void Controller_NPad::InitNewlyAddedController(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) {
+ styleset_changed_events[controller_idx].writable->Signal();
return;
}
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
+ controller.properties.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
+ break;
+ case NPadControllerType::ProController:
+ controller.joy_styles.pro_controller.Assign(1);
+ controller.device_type.pro_controller.Assign(1);
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NpadAssignments::Single;
+ break;
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
@@ -122,34 +159,26 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
+ controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
+ controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_plus.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
+ controller.pad_assignment = NpadAssignments::Single;
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.properties.is_vertical.Assign(1);
- controller.properties.use_plus.Assign(1);
- controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
+ controller.pad_assignment = NpadAssignments::Single;
break;
}
@@ -158,21 +187,25 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
- controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
- controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
- controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
+ controller.left_color.body_color =
+ Settings::values.players.GetValue()[controller_idx].body_color_left;
+ controller.left_color.button_color =
+ Settings::values.players.GetValue()[controller_idx].button_color_left;
+ controller.right_color.body_color =
+ Settings::values.players.GetValue()[controller_idx].body_color_right;
controller.right_color.button_color =
- Settings::values.players[controller_idx].button_color_right;
+ Settings::values.players.GetValue()[controller_idx].button_color_right;
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
- styleset_changed_events[controller_idx].writable->Signal();
+
+ SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
}
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
- for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
+ for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
}
@@ -181,6 +214,8 @@ void Controller_NPad::OnInit() {
return;
}
+ OnLoadInputDevices();
+
if (style.raw == 0) {
// We want to support all controllers
style.handheld.Assign(1);
@@ -191,42 +226,46 @@ void Controller_NPad::OnInit() {
style.pokeball.Assign(1);
}
- std::transform(
- Settings::values.players.begin(), Settings::values.players.end(),
- connected_controllers.begin(), [](const Settings::PlayerInput& player) {
- return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
- });
+ std::transform(Settings::values.players.GetValue().begin(),
+ Settings::values.players.GetValue().end(), connected_controllers.begin(),
+ [](const Settings::PlayerInput& player) {
+ return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
+ player.connected};
+ });
- std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
- [](const ControllerHolder& holder) { return holder.is_connected; });
+ // Connect the Player 1 or Handheld controller if none are connected.
+ if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
+ [](const ControllerHolder& controller) { return controller.is_connected; })) {
+ const auto controller =
+ MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
+ if (controller == NPadControllerType::Handheld) {
+ Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
+ connected_controllers[HANDHELD_INDEX] = {controller, true};
+ } else {
+ Settings::values.players.GetValue()[0].connected = true;
+ connected_controllers[0] = {controller, true};
+ }
+ }
// Account for handheld
- if (connected_controllers[8].is_connected)
- connected_controllers[8].type = NPadControllerType::Handheld;
+ if (connected_controllers[HANDHELD_INDEX].is_connected) {
+ connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
+ }
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));
- // Add a default dual joycon controller if none are present.
- if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
- [](const ControllerHolder& controller) { return controller.is_connected; })) {
- supported_npad_id_types.resize(npad_id_list.size());
- std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
- npad_id_list.size() * sizeof(u32));
- AddNewController(NPadControllerType::JoyDual);
- }
-
for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
const auto& controller = connected_controllers[i];
if (controller.is_connected) {
- AddNewControllerAt(controller.type, IndexToNPad(i));
+ AddNewControllerAt(controller.type, i);
}
}
}
void Controller_NPad::OnLoadInputDevices() {
- const auto& players = Settings::values.players;
+ const auto& players = Settings::values.players.GetValue();
for (std::size_t i = 0; i < players.size(); ++i) {
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
@@ -234,14 +273,30 @@ void Controller_NPad::OnLoadInputDevices() {
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
+ std::transform(players[i].vibrations.begin() +
+ Settings::NativeVibration::VIBRATION_HID_BEGIN,
+ players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
+ vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
+ std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
+ players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
+ motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
+ for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
+ InitializeVibrationDeviceAtIndex(i, device_idx);
+ }
}
}
-void Controller_NPad::OnRelease() {}
+void Controller_NPad::OnRelease() {
+ for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
+ for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
+ VibrateControllerAtIndex(npad_idx, device_idx, {});
+ }
+ }
+}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
- [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type;
+ const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
return;
}
@@ -256,61 +311,71 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
using namespace Settings::NativeButton;
- pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.l_stick_right.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::RIGHT));
- pad_state.l_stick_left.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::LEFT));
- pad_state.l_stick_up.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::UP));
- pad_state.l_stick_down.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::DOWN));
-
- pad_state.r_stick_right.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
- pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
- pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
- pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
-
- pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->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) {
+ pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.r_stick_right.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
+ pad_state.r_stick_left.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
+ pad_state.r_stick_up.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
+ pad_state.r_stick_down.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
+ 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::JoyRight) {
+ pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.l_stick_right.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
+ pad_state.l_stick_left.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
+ pad_state.l_stick_up.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
+ pad_state.l_stick_down.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
+ 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);
+ }
+
+ if (controller_type == NPadControllerType::JoyLeft ||
+ controller_type == NPadControllerType::JoyRight) {
+ pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
+ }
}
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t data_len) {
- if (!IsControllerActivated())
+ if (!IsControllerActivated()) {
return;
- for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
+ }
+ 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,
@@ -327,7 +392,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
const auto& last_entry =
main_controller->npad[main_controller->common.last_entry_index];
- main_controller->common.timestamp = core_timing.GetTicks();
+ main_controller->common.timestamp = core_timing.GetCPUTicks();
main_controller->common.last_entry_index =
(main_controller->common.last_entry_index + 1) % 17;
@@ -343,6 +408,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
continue;
}
const u32 npad_index = static_cast<u32>(i);
+
RequestPadStateUpdate(npad_index);
auto& pad_state = npad_pad_states[npad_index];
@@ -359,12 +425,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
libnx_entry.connection_status.raw = 0;
+ libnx_entry.connection_status.IsConnected.Assign(1);
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
+ 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.pad_states.raw = pad_state.pad_states.raw;
+ main_controller.pad.l_stick = pad_state.l_stick;
+ main_controller.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsWired.Assign(1);
+ break;
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
+ handheld_entry.connection_status.IsConnected.Assign(1);
handheld_entry.connection_status.IsWired.Assign(1);
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -373,57 +452,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
handheld_entry.pad.l_stick = pad_state.l_stick;
handheld_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsWired.Assign(1);
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
+ libnx_entry.connection_status.IsRightJoyWired.Assign(1);
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
-
+ dual_entry.connection_status.IsConnected.Assign(1);
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.pad_states.raw = pad_state.pad_states.raw;
dual_entry.pad.l_stick = pad_state.l_stick;
dual_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
-
left_entry.connection_status.IsConnected.Assign(1);
+ left_entry.connection_status.IsLeftJoyConnected.Assign(1);
left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
left_entry.pad.l_stick = pad_state.l_stick;
left_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
-
right_entry.connection_status.IsConnected.Assign(1);
+ right_entry.connection_status.IsRightJoyConnected.Assign(1);
right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
right_entry.pad.l_stick = pad_state.l_stick;
right_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
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.pad_states.raw = pad_state.pad_states.raw;
pokeball_entry.pad.l_stick = pad_state.l_stick;
pokeball_entry.pad.r_stick = pad_state.r_stick;
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.pad_states.raw = pad_state.pad_states.raw;
- main_controller.pad.l_stick = pad_state.l_stick;
- main_controller.pad.r_stick = pad_state.r_stick;
- break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -438,39 +512,144 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
shared_memory_entries.size() * sizeof(NPadEntry));
}
-void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
+void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, 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 auto& controller_type = connected_controllers[i].type;
+
+ if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
+ continue;
+ }
+
+ const std::array<SixAxisGeneric*, 6> controller_sixaxes{
+ &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
+ &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
+ };
+
+ for (auto* sixaxis_sensor : controller_sixaxes) {
+ sixaxis_sensor->common.entry_count = 16;
+ sixaxis_sensor->common.total_entry_count = 17;
+
+ const auto& last_entry =
+ sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+ sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
+ sixaxis_sensor->common.last_entry_index =
+ (sixaxis_sensor->common.last_entry_index + 1) % 17;
+
+ auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+ cur_entry.timestamp = last_entry.timestamp + 1;
+ cur_entry.timestamp2 = cur_entry.timestamp;
+ }
+
+ // Try to read sixaxis sensor states
+ std::array<MotionDevice, 2> motion_devices;
+
+ if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
+ sixaxis_at_rest = true;
+ for (std::size_t e = 0; e < motion_devices.size(); ++e) {
+ const auto& device = motions[i][e];
+ if (device) {
+ std::tie(motion_devices[e].accel, motion_devices[e].gyro,
+ motion_devices[e].rotation, motion_devices[e].orientation) =
+ device->GetStatus();
+ sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
+ }
+ }
+ }
+
+ auto& full_sixaxis_entry =
+ npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
+ auto& handheld_sixaxis_entry =
+ npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
+ auto& dual_left_sixaxis_entry =
+ npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
+ auto& dual_right_sixaxis_entry =
+ npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
+ auto& left_sixaxis_entry =
+ npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
+ auto& right_sixaxis_entry =
+ npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
+
+ switch (controller_type) {
+ case NPadControllerType::None:
+ UNREACHABLE();
+ break;
+ case NPadControllerType::ProController:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ full_sixaxis_entry.accel = motion_devices[0].accel;
+ full_sixaxis_entry.gyro = motion_devices[0].gyro;
+ full_sixaxis_entry.rotation = motion_devices[0].rotation;
+ full_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ break;
+ case NPadControllerType::Handheld:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ handheld_sixaxis_entry.accel = motion_devices[0].accel;
+ handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
+ handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
+ handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ break;
+ case NPadControllerType::JoyDual:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ // Set motion for the left joycon
+ dual_left_sixaxis_entry.accel = motion_devices[0].accel;
+ dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
+ dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
+ dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ if (sixaxis_sensors_enabled && motions[i][1]) {
+ // Set motion for the right joycon
+ dual_right_sixaxis_entry.accel = motion_devices[1].accel;
+ dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
+ dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
+ dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
+ }
+ break;
+ case NPadControllerType::JoyLeft:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ left_sixaxis_entry.accel = motion_devices[0].accel;
+ left_sixaxis_entry.gyro = motion_devices[0].gyro;
+ left_sixaxis_entry.rotation = motion_devices[0].rotation;
+ left_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ break;
+ case NPadControllerType::JoyRight:
+ if (sixaxis_sensors_enabled && motions[i][1]) {
+ right_sixaxis_entry.accel = motion_devices[1].accel;
+ right_sixaxis_entry.gyro = motion_devices[1].gyro;
+ right_sixaxis_entry.rotation = motion_devices[1].rotation;
+ right_sixaxis_entry.orientation = motion_devices[1].orientation;
+ }
+ break;
+ case NPadControllerType::Pokeball:
+ break;
+ }
+ }
+ std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
+ shared_memory_entries.size() * sizeof(NPadEntry));
+}
+
+void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
style.raw = style_set.raw;
}
-Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
+Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
return style;
}
-void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
+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;
- }
- const auto requested_controller =
- i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
- : NPadControllerType::Handheld;
- if (!IsControllerSupported(requested_controller)) {
- const auto is_handheld = requested_controller == NPadControllerType::Handheld;
- if (is_handheld) {
- controller.type = NPadControllerType::None;
- controller.is_connected = false;
- AddNewController(requested_controller);
- } else {
- controller.type = requested_controller;
- InitNewlyAddedControler(i);
- }
- }
- }
}
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -478,7 +657,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length)
std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
}
-std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
+std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
return supported_npad_id_types.size();
}
@@ -490,7 +669,15 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
return hold_type;
}
-void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
+void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
+ handheld_activation_mode = activation_mode;
+}
+
+Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const {
+ return handheld_activation_mode;
+}
+
+void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
@@ -498,70 +685,230 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
}
}
-void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
- const std::vector<Vibration>& vibrations) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
+ const VibrationValue& vibration_value) {
+ if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
+ return false;
+ }
+
+ const auto& player = Settings::values.players.GetValue()[npad_index];
- if (!can_controllers_vibrate) {
- return;
+ if (!player.vibration_enabled) {
+ if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
+ latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
+ // Send an empty vibration to stop any vibrations.
+ vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
+ // Then reset the vibration value to its default value.
+ latest_vibration_values[npad_index][device_index] = {};
+ }
+
+ return false;
}
- for (std::size_t i = 0; i < controller_ids.size(); i++) {
- std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
- if (connected_controllers[controller_pos].is_connected) {
- // TODO(ogniK): Vibrate the physical controller
+
+ if (!Settings::values.enable_accurate_vibrations.GetValue()) {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::steady_clock;
+
+ const auto now = steady_clock::now();
+
+ // Filter out non-zero vibrations that are within 10ms of each other.
+ if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
+ duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
+ milliseconds(10)) {
+ return false;
}
+
+ last_vibration_timepoints[npad_index][device_index] = now;
}
- last_processed_vibration = vibrations.back();
+
+ auto& vibration = vibrations[npad_index][device_index];
+ const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
+ const auto amp_low =
+ std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
+ const auto amp_high =
+ std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
+ return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
+ vibration_value.freq_high);
+}
+
+void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
+ const VibrationValue& vibration_value) {
+ if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
+ return;
+ }
+
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+
+ if (!vibration_devices_mounted[npad_index][device_index] ||
+ !connected_controllers[npad_index].is_connected) {
+ return;
+ }
+
+ if (vibration_device_handle.device_index == DeviceIndex::None) {
+ UNREACHABLE_MSG("DeviceIndex should never be None!");
+ return;
+ }
+
+ // Some games try to send mismatched parameters in the device handle, block these.
+ if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
+ (vibration_device_handle.npad_type == NpadType::JoyconRight ||
+ vibration_device_handle.device_index == DeviceIndex::Right)) ||
+ (connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
+ (vibration_device_handle.npad_type == NpadType::JoyconLeft ||
+ vibration_device_handle.device_index == DeviceIndex::Left))) {
+ return;
+ }
+
+ // Filter out vibrations with equivalent values to reduce unnecessary state changes.
+ if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
+ vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
+ return;
+ }
+
+ if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
+ latest_vibration_values[npad_index][device_index] = vibration_value;
+ }
+}
+
+void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
+ const std::vector<VibrationValue>& vibration_values) {
+ if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
+ return;
+ }
+
+ ASSERT_OR_EXECUTE_MSG(
+ vibration_device_handles.size() == vibration_values.size(), { return; },
+ "The amount of device handles does not match with the amount of vibration values,"
+ "this is undefined behavior!");
+
+ for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
+ VibrateController(vibration_device_handles[i], vibration_values[i]);
+ }
+}
+
+Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
+ const DeviceHandle& vibration_device_handle) const {
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+ return latest_vibration_values[npad_index][device_index];
+}
+
+void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+ InitializeVibrationDeviceAtIndex(npad_index, device_index);
+}
+
+void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
+ std::size_t device_index) {
+ if (vibrations[npad_index][device_index]) {
+ vibration_devices_mounted[npad_index][device_index] =
+ vibrations[npad_index][device_index]->GetStatus() == 1;
+ } else {
+ vibration_devices_mounted[npad_index][device_index] = false;
+ }
+}
+
+void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
+ permit_vibration_session_enabled = permit_vibration_session;
+}
+
+bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+ return vibration_devices_mounted[npad_index][device_index];
}
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
- // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
- // be signalled at least once, and signaled after a new controller is connected?
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
return styleset_event.readable;
}
-Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
- return last_processed_vibration;
+void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
+ styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal();
}
-void Controller_NPad::AddNewController(NPadControllerType controller) {
- controller = DecideBestController(controller);
- if (controller == NPadControllerType::Handheld) {
- connected_controllers[8] = {controller, true};
- InitNewlyAddedControler(8);
+void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
+ UpdateControllerAt(controller, npad_index, true);
+}
+
+void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
+ bool connected) {
+ if (!connected) {
+ DisconnectNpadAtIndex(npad_index);
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!");
+
+ if (controller == NPadControllerType::Handheld) {
+ Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
+ MapNPadToSettingsType(controller);
+ Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
+ connected_controllers[HANDHELD_INDEX] = {controller, true};
+ InitNewlyAddedController(HANDHELD_INDEX);
return;
}
- const auto controller_id = std::distance(connected_controllers.begin(), pos);
- connected_controllers[controller_id] = {controller, true};
- InitNewlyAddedControler(controller_id);
+
+ Settings::values.players.GetValue()[npad_index].controller_type =
+ MapNPadToSettingsType(controller);
+ Settings::values.players.GetValue()[npad_index].connected = true;
+ connected_controllers[npad_index] = {controller, true};
+ InitNewlyAddedController(npad_index);
}
-void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
- controller = DecideBestController(controller);
- if (controller == NPadControllerType::Handheld) {
- connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
- InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
- return;
+void Controller_NPad::DisconnectNpad(u32 npad_id) {
+ DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
+}
+
+void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
+ for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
+ // Send an empty vibration to stop any vibrations.
+ VibrateControllerAtIndex(npad_index, device_idx, {});
+ vibration_devices_mounted[npad_index][device_idx] = false;
}
- connected_controllers[NPadIdToIndex(npad_id)] = {controller, true};
- InitNewlyAddedControler(NPadIdToIndex(npad_id));
+ Settings::values.players.GetValue()[npad_index].connected = false;
+ connected_controllers[npad_index].is_connected = false;
+
+ auto& controller = shared_memory_entries[npad_index];
+ controller.joy_styles.raw = 0; // Zero out
+ controller.device_type.raw = 0;
+ controller.properties.raw = 0;
+
+ SignalStyleSetChangedEvent(IndexToNPad(npad_index));
+}
+
+void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
+ gyroscope_zero_drift_mode = drift_mode;
+}
+
+Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const {
+ return gyroscope_zero_drift_mode;
+}
+
+bool Controller_NPad::IsSixAxisSensorAtRest() const {
+ return sixaxis_at_rest;
}
-void Controller_NPad::ConnectNPad(u32 npad_id) {
- connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
+void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
+ sixaxis_sensors_enabled = six_axis_status;
}
-void Controller_NPad::DisconnectNPad(u32 npad_id) {
- connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
+void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
+ const auto npad_index_1 = NPadIdToIndex(npad_id_1);
+ const auto npad_index_2 = NPadIdToIndex(npad_id_2);
+
+ // If the controllers at both npad indices form a pair of left and right joycons, merge them.
+ // Otherwise, do nothing.
+ if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft &&
+ connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) ||
+ (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
+ connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
+ // Disconnect the joycon at the second id and connect the dual joycon at the first index.
+ DisconnectNpad(npad_id_2);
+ AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
+ }
}
void Controller_NPad::StartLRAssignmentMode() {
@@ -589,8 +936,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
- InitNewlyAddedControler(npad_index_1);
- InitNewlyAddedControler(npad_index_2);
+ AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
+ AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
return true;
}
@@ -604,11 +951,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
case 0:
return LedPattern{1, 0, 0, 0};
case 1:
- return LedPattern{0, 1, 0, 0};
+ return LedPattern{1, 1, 0, 0};
case 2:
- return LedPattern{0, 0, 1, 0};
+ return LedPattern{1, 1, 1, 0};
case 3:
- return LedPattern{0, 0, 0, 1};
+ return LedPattern{1, 1, 1, 1};
case 4:
return LedPattern{1, 0, 0, 1};
case 5:
@@ -618,17 +965,17 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
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::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const {
+ return unintended_home_button_input_protection[NPadIdToIndex(npad_id)];
}
-bool Controller_NPad::IsVibrationEnabled() const {
- return can_controllers_vibrate;
+void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
+ u32 npad_id) {
+ unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
}
void Controller_NPad::ClearAllConnectedControllers() {
@@ -641,13 +988,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
}
void Controller_NPad::DisconnectAllConnectedControllers() {
- for (ControllerHolder& controller : connected_controllers) {
+ for (auto& controller : connected_controllers) {
controller.is_connected = false;
}
}
void Controller_NPad::ConnectAllDisconnectedControllers() {
- for (ControllerHolder& controller : connected_controllers) {
+ for (auto& controller : connected_controllers) {
if (controller.type != NPadControllerType::None && !controller.is_connected) {
controller.is_connected = true;
}
@@ -655,7 +1002,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
}
void Controller_NPad::ClearAllControllers() {
- for (ControllerHolder& controller : connected_controllers) {
+ for (auto& controller : connected_controllers) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
}
@@ -675,7 +1022,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
// Handheld should not be supported in docked mode
- if (Settings::values.use_docked_mode) {
+ if (Settings::values.use_docked_mode.GetValue()) {
return false;
}
@@ -703,92 +1050,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
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);
- break;
- }
-
- 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
index 931f03430..160dcbbe3 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -32,51 +32,90 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ // When the controller is requesting a motion update for the shared memory
+ void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+ std::size_t size) override;
+
// Called when input devices should be loaded
void OnLoadInputDevices() override;
- struct NPadType {
- union {
- u32_le raw{};
+ enum class NPadControllerType {
+ None,
+ ProController,
+ Handheld,
+ JoyDual,
+ JoyLeft,
+ JoyRight,
+ Pokeball,
+ };
- BitField<0, 1, u32> pro_controller;
- BitField<1, 1, u32> handheld;
- BitField<2, 1, u32> joycon_dual;
- BitField<3, 1, u32> joycon_left;
- BitField<4, 1, u32> joycon_right;
+ enum class NpadType : u8 {
+ ProController = 3,
+ Handheld = 4,
+ JoyconDual = 5,
+ JoyconLeft = 6,
+ JoyconRight = 7,
+ Pokeball = 9,
+ };
- BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
- };
+ enum class DeviceIndex : u8 {
+ Left = 0,
+ Right = 1,
+ None = 2,
};
- static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
- struct Vibration {
- f32 amp_low;
- f32 freq_low;
- f32 amp_high;
- f32 freq_high;
+ enum class GyroscopeZeroDriftMode : u32 {
+ Loose = 0,
+ Standard = 1,
+ Tight = 2,
};
- static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
enum class NpadHoldType : u64 {
Vertical = 0,
Horizontal = 1,
};
- enum class NPadAssignments : u32_le {
+ enum class NpadAssignments : u32 {
Dual = 0,
Single = 1,
};
- enum class NPadControllerType {
- None,
- ProController,
- Handheld,
- JoyDual,
- JoyLeft,
- JoyRight,
- Pokeball,
+ enum class NpadHandheldActivationMode : u64 {
+ Dual = 0,
+ Single = 1,
+ None = 2,
+ };
+
+ struct DeviceHandle {
+ NpadType npad_type{};
+ u8 npad_id{};
+ DeviceIndex device_index{};
+ INSERT_PADDING_BYTES(1);
};
+ static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
+
+ struct NpadStyleSet {
+ union {
+ u32_le raw{};
+
+ BitField<0, 1, u32> pro_controller;
+ BitField<1, 1, u32> handheld;
+ BitField<2, 1, u32> joycon_dual;
+ BitField<3, 1, u32> joycon_left;
+ BitField<4, 1, u32> joycon_right;
+
+ BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
+ };
+ };
+ static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
+
+ struct VibrationValue {
+ f32 amp_low{0.0f};
+ f32 freq_low{160.0f};
+ f32 amp_high{0.0f};
+ f32 freq_high{320.0f};
+ };
+ static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
@@ -94,37 +133,64 @@ public:
};
};
- void SetSupportedStyleSet(NPadType style_set);
- NPadType GetSupportedStyleSet() const;
+ void SetSupportedStyleSet(NpadStyleSet style_set);
+ NpadStyleSet GetSupportedStyleSet() const;
- void SetSupportedNPadIdTypes(u8* data, std::size_t length);
+ void SetSupportedNpadIdTypes(u8* data, std::size_t length);
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
- std::size_t GetSupportedNPadIdTypesSize() const;
+ std::size_t GetSupportedNpadIdTypesSize() const;
void SetHoldType(NpadHoldType joy_hold_type);
NpadHoldType GetHoldType() const;
- void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
+ void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
+ NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
+
+ void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
- void VibrateController(const std::vector<u32>& controller_ids,
- const std::vector<Vibration>& vibrations);
+ bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
+ const VibrationValue& vibration_value);
+
+ void VibrateController(const DeviceHandle& vibration_device_handle,
+ const VibrationValue& vibration_value);
+
+ void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
+ const std::vector<VibrationValue>& vibration_values);
+
+ VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
+
+ void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
+
+ void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
+
+ void SetPermitVibrationSession(bool permit_vibration_session);
+
+ bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
- Vibration GetLastVibration() const;
+ void SignalStyleSetChangedEvent(u32 npad_id) const;
+
+ // Adds a new controller at an index.
+ void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
+ // Adds a new controller at an index with connection status.
+ void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
- void AddNewController(NPadControllerType controller);
- void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
+ void DisconnectNpad(u32 npad_id);
+ void DisconnectNpadAtIndex(std::size_t index);
- void ConnectNPad(u32 npad_id);
- void DisconnectNPad(u32 npad_id);
+ void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
+ GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
+ bool IsSixAxisSensorAtRest() const;
+ void SetSixAxisEnabled(bool six_axis_status);
LedPattern GetLedPattern(u32 npad_id);
- void SetVibrationEnabled(bool can_vibrate);
- bool IsVibrationEnabled() const;
+ bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
+ void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
+ void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2);
@@ -133,6 +199,8 @@ public:
// Specifically for cheat engine and other features.
u32 GetAndResetPressState();
+ static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
+ static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
@@ -236,6 +304,24 @@ private:
};
static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
+ struct SixAxisStates {
+ s64_le timestamp{};
+ INSERT_PADDING_WORDS(2);
+ s64_le timestamp2{};
+ Common::Vec3f accel{};
+ Common::Vec3f gyro{};
+ Common::Vec3f rotation{};
+ std::array<Common::Vec3f, 3> orientation{};
+ s64_le always_one{1};
+ };
+ static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
+
+ struct SixAxisGeneric {
+ CommonHeader common{};
+ std::array<SixAxisStates, 17> sixaxis{};
+ };
+ static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
+
enum class ColorReadError : u32_le {
ReadOk = 0,
ColorDoesntExist = 1,
@@ -265,9 +351,16 @@ private:
};
};
+ struct MotionDevice {
+ Common::Vec3f accel;
+ Common::Vec3f gyro;
+ Common::Vec3f rotation;
+ std::array<Common::Vec3f, 3> orientation;
+ };
+
struct NPadEntry {
- NPadType joy_styles;
- NPadAssignments pad_assignment;
+ NpadStyleSet joy_styles;
+ NpadAssignments pad_assignment;
ColorReadError single_color_error;
ControllerColor single_color;
@@ -284,9 +377,12 @@ private:
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
+ SixAxisGeneric sixaxis_full;
+ SixAxisGeneric sixaxis_handheld;
+ SixAxisGeneric sixaxis_dual_left;
+ SixAxisGeneric sixaxis_dual_right;
+ SixAxisGeneric sixaxis_left;
+ SixAxisGeneric sixaxis_right;
NPadDevice device_type;
NPadProperties properties;
INSERT_PADDING_WORDS(1);
@@ -301,31 +397,44 @@ private:
bool is_connected;
};
- void InitNewlyAddedControler(std::size_t controller_idx);
+ void InitNewlyAddedController(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
- NPadControllerType DecideBestController(NPadControllerType priority) const;
void RequestPadStateUpdate(u32 npad_id);
u32 press_state{};
- NPadType style{};
+ NpadStyleSet style{};
std::array<NPadEntry, 10> shared_memory_entries{};
- std::array<
+ using ButtonArray = std::array<
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
- 10>
- buttons;
- std::array<
+ 10>;
+ using StickArray = std::array<
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
- 10>
- sticks;
+ 10>;
+ using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
+ Settings::NativeVibration::NUM_VIBRATIONS_HID>,
+ 10>;
+ using MotionArray = std::array<
+ std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
+ 10>;
+ ButtonArray buttons;
+ StickArray sticks;
+ VibrationArray vibrations;
+ MotionArray motions;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
+ NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
// Each controller should have their own styleset changed event
std::array<Kernel::EventPair, 10> styleset_changed_events;
- Vibration last_processed_vibration{};
+ std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints;
+ std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
+ bool permit_vibration_session_enabled{false};
+ std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
std::array<ControllerHolder, 10> connected_controllers{};
- bool can_controllers_vibrate{true};
-
+ std::array<bool, 10> unintended_home_button_input_protection{};
+ GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+ bool sixaxis_sensors_enabled{true};
+ bool sixaxis_at_rest{true};
std::array<ControllerPad, 10> npad_pad_states{};
bool is_in_lr_assignment_mode{false};
Core::System& system;
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 9e527d176..e7483bfa2 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -23,7 +23,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
}
CommonHeader header{};
- header.timestamp = core_timing.GetTicks();
+ header.timestamp = core_timing.GetCPUTicks();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 1c6e55566..0df395e85 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -22,7 +22,7 @@ void Controller_Touchscreen::OnRelease() {}
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetTicks();
+ shared_memory.header.timestamp = core_timing.GetCPUTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -40,16 +40,21 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
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();
+ bool pressed = false;
+ float x, y;
+ std::tie(x, y, pressed) = touch_device->GetStatus();
auto& touch_entry = cur_entry.states[0];
touch_entry.attribute.raw = 0;
+ if (!pressed && touch_btn_device) {
+ std::tie(x, y, pressed) = touch_btn_device->GetStatus();
+ }
if (pressed && Settings::values.touchscreen.enabled) {
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
- const u64 tick = core_timing.GetTicks();
+ const u64 tick = core_timing.GetCPUTicks();
touch_entry.delta_time = tick - last_touch;
last_touch = tick;
touch_entry.finger = Settings::values.touchscreen.finger;
@@ -63,5 +68,10 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
void Controller_Touchscreen::OnLoadInputDevices() {
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
+ if (Settings::values.use_touch_from_button) {
+ touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
+ } else {
+ touch_btn_device.reset();
+ }
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index a1d97269e..4d9042adc 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -68,6 +68,7 @@ private:
"TouchScreenSharedMemory is an invalid size");
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
+ std::unique_ptr<Input::TouchDevice> touch_btn_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
index 27511b27b..2503ef241 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -20,7 +20,7 @@ void Controller_XPad::OnRelease() {}
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
- xpad_entry.header.timestamp = core_timing.GetTicks();
+ xpad_entry.header.timestamp = core_timing.GetCPUTicks();
xpad_entry.header.total_entry_count = 17;
if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d6ed5f304..902516b29 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -14,6 +14,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/writable_event.h"
@@ -37,12 +38,10 @@
namespace Service::HID {
// Updating period for each HID device.
-// TODO(ogniK): Find actual polling rate of hid
-constexpr s64 pad_update_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 66);
-[[maybe_unused]] constexpr s64 accelerometer_update_ticks =
- static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100);
-[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
- static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100);
+// HID is polled every 15ms, this value was derived from
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
+constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
+constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -53,9 +52,7 @@ IAppletResource::IAppletResource(Core::System& system)
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- shared_mem = Kernel::SharedMemory::Create(
- kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
+ shared_mem = SharedFrom(&kernel.GetHidSharedMem());
MakeController<Controller_DebugPad>(HidController::DebugPad);
MakeController<Controller_Touchscreen>(HidController::Touchscreen);
@@ -78,14 +75,19 @@ IAppletResource::IAppletResource(Core::System& system)
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
// Register update callbacks
- pad_update_event =
- Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
- UpdateControllers(userdata, cycles_late);
+ pad_update_event = Core::Timing::CreateEvent(
+ "HID::UpdatePadCallback",
+ [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ UpdateControllers(user_data, ns_late);
+ });
+ motion_update_event = Core::Timing::CreateEvent(
+ "HID::MotionPadCallback",
+ [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ UpdateMotion(user_data, ns_late);
});
- // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
-
- system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event);
+ system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
+ system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
ReloadInputDevices();
}
@@ -110,7 +112,8 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
rb.PushCopyObjects(shared_mem);
}
-void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
+void IAppletResource::UpdateControllers(std::uintptr_t user_data,
+ std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
@@ -121,25 +124,49 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
- core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
+ core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
+}
+
+void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ auto& core_timing = system.CoreTiming();
+
+ for (const auto& controller : controllers) {
+ controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
+ }
+
+ core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
}
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
public:
- IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") {
+ explicit IActiveVibrationDeviceList(std::shared_ptr<IAppletResource> applet_resource_)
+ : ServiceFramework("IActiveVibrationDeviceList"), applet_resource(applet_resource_) {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"},
+ {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
private:
- void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .InitializeVibrationDevice(vibration_device_handle);
+
+ LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
+ vibration_device_handle.npad_type, vibration_device_handle.npad_id,
+ vibration_device_handle.device_index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
+ std::shared_ptr<IAppletResource> applet_resource;
};
std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
@@ -158,16 +185,16 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
{21, &Hid::ActivateMouse, "ActivateMouse"},
{31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
- {32, nullptr, "SendKeyboardLockKeyEvent"},
+ {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
{40, nullptr, "AcquireXpadIdEventHandle"},
{41, nullptr, "ReleaseXpadIdEventHandle"},
{51, &Hid::ActivateXpad, "ActivateXpad"},
- {55, nullptr, "GetXpadIds"},
+ {55, &Hid::GetXpadIDs, "GetXpadIds"},
{56, nullptr, "ActivateJoyXpad"},
{58, nullptr, "GetJoyXpadLifoHandle"},
{59, nullptr, "GetJoyXpadIds"},
- {60, nullptr, "ActivateSixAxisSensor"},
- {61, nullptr, "DeactivateSixAxisSensor"},
+ {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
+ {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
{62, nullptr, "GetSixAxisSensorLifoHandle"},
{63, nullptr, "ActivateJoySixAxisSensor"},
{64, nullptr, "DeactivateJoySixAxisSensor"},
@@ -175,7 +202,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
{68, nullptr, "IsSixAxisSensorFusionEnabled"},
- {69, nullptr, "EnableSixAxisSensorFusion"},
+ {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
{70, nullptr, "SetSixAxisSensorFusionParameters"},
{71, nullptr, "GetSixAxisSensorFusionParameters"},
{72, nullptr, "ResetSixAxisSensorFusionParameters"},
@@ -186,8 +213,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{77, nullptr, "GetAccelerometerPlayMode"},
{78, nullptr, "ResetAccelerometerPlayMode"},
{79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
- {80, nullptr, "GetGyroscopeZeroDriftMode"},
- {81, nullptr, "ResetGyroscopeZeroDriftMode"},
+ {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
+ {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
{83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
@@ -211,8 +238,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
{129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
{130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
- {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
- {132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
+ {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
+ {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
{134, nullptr, "SetNpadAnalogStickUseCenterClamp"},
{135, nullptr, "SetNpadCaptureButtonAssignment"},
@@ -228,18 +255,18 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{208, nullptr, "GetActualVibrationGcErmCommand"},
{209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
- {211, nullptr, "IsVibrationDeviceMounted"},
+ {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
{300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
{301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
- {302, nullptr, "StopConsoleSixAxisSensor"},
- {303, nullptr, "ActivateSevenSixAxisSensor"},
- {304, nullptr, "StartSevenSixAxisSensor"},
- {305, nullptr, "StopSevenSixAxisSensor"},
+ {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
+ {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
+ {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
+ {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
{306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
- {307, nullptr, "FinalizeSevenSixAxisSensor"},
+ {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
- {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
+ {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
{401, nullptr, "EnableUsbFullKeyController"},
{402, nullptr, "IsUsbFullKeyControllerConnected"},
@@ -247,7 +274,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{404, nullptr, "HasLeftRightBattery"},
{405, nullptr, "GetNpadInterfaceType"},
{406, nullptr, "GetNpadLeftRightInterfaceType"},
- {407, nullptr, "GetNpadOfHighestBatteryLevelForJoyLeft"},
+ {407, nullptr, "GetNpadOfHighestBatteryLevel"},
{408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
{500, nullptr, "GetPalmaConnectionHandle"},
{501, nullptr, "InitializePalma"},
@@ -283,6 +310,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{1001, nullptr, "GetNpadCommunicationMode"},
{1002, nullptr, "SetTouchScreenConfiguration"},
{1003, nullptr, "IsFirmwareUpdateNeededForNotification"},
+ {2000, nullptr, "ActivateDigitizer"},
};
// clang-format on
@@ -306,97 +334,195 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IAppletResource>(applet_resource);
}
-void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto basic_xpad_id{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", basic_xpad_id,
- applet_resource_user_id);
+ applet_resource->ActivateController(HidController::DebugPad);
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::XPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->ActivateController(HidController::Touchscreen);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::DebugPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->ActivateController(HidController::Mouse);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::Touchscreen);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->ActivateController(HidController::Keyboard);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::Mouse);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) {
+void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto flags{rp.Pop<u32>()};
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
- applet_resource->ActivateController(HidController::Keyboard);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto unknown{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 basic_xpad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->ActivateController(HidController::XPad);
+
+ LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
+ parameters.basic_xpad_id, parameters.applet_resource_user_id);
- applet_resource->ActivateController(HidController::Gesture);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
- // Should have no effect with how our npad sets up the data
+void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto unknown{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
- applet_resource_user_id);
+ LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(0);
+}
+
+void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
- applet_resource->ActivateController(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool enable_sixaxis_sensor_fusion{};
+ INSERT_PADDING_BYTES(3);
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
+ "device_index={}, applet_resource_user_id={}",
+ parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
+ parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -404,13 +530,61 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto drift_mode{rp.Pop<u32>()};
+ const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+ const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID,
- "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}", handle,
- drift_mode, applet_resource_user_id);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetGyroscopeZeroDriftMode(drift_mode);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
+ "applet_resource_user_id={}",
+ sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
+ drift_mode, applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetGyroscopeZeroDriftMode());
+}
+
+void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -418,27 +592,53 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- // TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
- rb.Push(true);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .IsSixAxisSensorAtRest());
+}
+
+void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ u32 unknown{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->ActivateController(HidController::Gesture);
+
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
+ parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto supported_styleset{rp.Pop<u32>()};
- LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
-
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetSupportedStyleSet({supported_styleset});
+ LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -449,21 +649,22 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(controller.GetSupportedStyleSet().raw);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetSupportedStyleSet()
+ .raw);
}
void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -472,48 +673,62 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->ActivateController(HidController::NPad);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- applet_resource->ActivateController(HidController::NPad);
}
void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->DeactivateController(HidController::NPad);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- applet_resource->DeactivateController(HidController::NPad);
}
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto unknown{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ u64 unknown{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
- LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", npad_id,
- applet_resource_user_id, unknown);
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
+ parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetStyleSetChangedEvent(npad_id));
+ .GetStyleSetChangedEvent(parameters.npad_id));
}
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .DisconnectNpad(parameters.npad_id);
+
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
- applet_resource->GetController<Controller_NPad>(HidController::NPad).DisconnectNPad(npad_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -526,22 +741,41 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetLedPattern(npad_id)
- .raw);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetLedPattern(npad_id)
+ .raw);
+}
+
+void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
+ // Should have no effect with how our npad sets up the data
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ u32 unknown{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->ActivateController(HidController::NPad);
+
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
+ parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto hold_type{rp.Pop<u64>()};
+ const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
applet_resource_user_id, hold_type);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
-
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -552,22 +786,26 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- const auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
+ rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType());
}
void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -576,16 +814,22 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto npad_joy_device_type{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ u64 npad_joy_device_type{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
LOG_WARNING(Service_HID,
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
- npad_id, applet_resource_user_id, npad_joy_device_type);
-
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
+ parameters.npad_id, parameters.applet_resource_user_id,
+ parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -593,14 +837,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual);
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -608,13 +857,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto unknown_1{rp.Pop<u32>()};
- const auto unknown_2{rp.Pop<u32>()};
+ const auto npad_id_1{rp.Pop<u32>()};
+ const auto npad_id_2{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID,
- "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
- unknown_1, unknown_2, applet_resource_user_id);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
+
+ LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
+ npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -624,9 +875,9 @@ void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode();
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.StartLRAssignmentMode();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -636,9 +887,9 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode();
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.StopLRAssignmentMode();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -647,10 +898,13 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto mode{rp.Pop<u64>()};
+ const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadHandheldActivationMode(activation_mode);
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
- applet_resource_user_id, mode);
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
+ applet_resource_user_id, activation_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -660,25 +914,28 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
- applet_resource_user_id);
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetNpadHandheldActivationMode());
}
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_1{rp.Pop<u32>()};
- const auto npad_2{rp.Pop<u32>()};
+ const auto npad_id_1{rp.Pop<u32>()};
+ const auto npad_id_2{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
- applet_resource_user_id, npad_1, npad_2);
+ const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SwapNpadAssignment(npad_id_1, npad_id_2);
+
+ LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
+ npad_id_1, npad_id_2, applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 2};
- if (controller.SwapNpadAssignment(npad_1, npad_2)) {
+ if (res) {
rb.Push(RESULT_SUCCESS);
} else {
LOG_ERROR(Service_HID, "Npads are not connected!");
@@ -686,119 +943,275 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
}
}
-void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(true);
- IPC::ResponseBuilder rb{ctx, 2};
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
}
-void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool unintended_home_button_input_protection{};
+ INSERT_PADDING_BYTES(3);
+ u32 npad_id{};
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetUnintendedHomeButtonInputProtectionEnabled(
+ parameters.unintended_home_button_input_protection, parameters.npad_id);
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
+ "applet_resource_user_id={}",
+ parameters.unintended_home_button_input_protection, parameters.npad_id,
+ parameters.applet_resource_user_id);
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+
+ VibrationDeviceInfo vibration_device_info;
+
+ vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
+
+ switch (vibration_device_handle.device_index) {
+ case Controller_NPad::DeviceIndex::Left:
+ vibration_device_info.position = VibrationDevicePosition::Left;
+ break;
+ case Controller_NPad::DeviceIndex::Right:
+ vibration_device_info.position = VibrationDevicePosition::Right;
+ break;
+ case Controller_NPad::DeviceIndex::None:
+ default:
+ UNREACHABLE_MSG("DeviceIndex should never be None!");
+ vibration_device_info.position = VibrationDevicePosition::None;
+ break;
+ }
+
+ LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
+ vibration_device_info.type, vibration_device_info.position);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(vibration_device_info);
+}
+
void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto controller_id{rp.Pop<u32>()};
- const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle vibration_device_handle{};
+ Controller_NPad::VibrationValue vibration_value{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .VibrateController(parameters.vibration_device_handle, parameters.vibration_value);
- LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id,
- applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.vibration_device_handle.npad_type,
+ parameters.vibration_device_handle.npad_id,
+ parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
+}
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .VibrateController({controller_id}, {vibration_values});
+void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle vibration_device_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.vibration_device_handle.npad_type,
+ parameters.vibration_device_handle.npad_id,
+ parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetLastVibration(parameters.vibration_device_handle));
+}
+
+void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IActiveVibrationDeviceList>(applet_resource);
+}
+
+void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto can_vibrate{rp.Pop<bool>()};
+
+ Settings::values.vibration_enabled.SetValue(can_vibrate);
+
+ LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(Settings::values.vibration_enabled.GetValue());
}
void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
-
- const auto controllers = ctx.ReadBuffer(0);
+ const auto handles = 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::vector<Controller_NPad::DeviceHandle> vibration_device_handles(
+ handles.size() / sizeof(Controller_NPad::DeviceHandle));
+ std::vector<Controller_NPad::VibrationValue> vibration_values(
+ vibrations.size() / sizeof(Controller_NPad::VibrationValue));
- 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; });
+ std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
+ std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .VibrateController(controller_list, vibration_list);
+ .VibrateControllers(vibration_device_handles, vibration_values);
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
+void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto controller_id{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id,
- applet_resource_user_id);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetPermitVibrationSession(true);
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- IPC::ResponseBuilder rb{ctx, 6};
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<Controller_NPad::Vibration>(
- applet_resource->GetController<Controller_NPad>(HidController::NPad).GetLastVibration());
}
-void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
+void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetPermitVibrationSession(false);
+
LOG_DEBUG(Service_HID, "called");
- IPC::ResponseBuilder rb{ctx, 4};
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(1);
- rb.Push<u32>(0);
}
-void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle vibration_device_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.vibration_device_handle.npad_type,
+ parameters.vibration_device_handle.npad_id,
+ parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IActiveVibrationDeviceList>();
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .IsVibrationDeviceMounted(parameters.vibration_device_handle));
}
-void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto can_vibrate{rp.Pop<bool>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetVibrationEnabled(can_vibrate);
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- IPC::ResponseBuilder rb{ctx, 3};
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.Push(
- applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
}
-void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
+void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -809,52 +1222,75 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
-void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
+void Hid::StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
+void Hid::StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle);
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
+void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto unknown{rp.Pop<u32>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}",
- applet_resource_user_id, unknown);
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
+void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto unknown{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown);
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto is_palma_all_connectable{rp.Pop<bool>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}",
+ applet_resource_user_id, is_palma_all_connectable);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto palma_boost_mode{rp.Pop<bool>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -871,6 +1307,7 @@ public:
{10, nullptr, "DeactivateTouchScreen"},
{11, nullptr, "SetTouchScreenAutoPilotState"},
{12, nullptr, "UnsetTouchScreenAutoPilotState"},
+ {13, nullptr, "GetTouchScreenConfiguration"},
{20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"},
@@ -880,7 +1317,9 @@ public:
{50, nullptr, "DeactivateXpad"},
{51, nullptr, "SetXpadAutoPilotState"},
{52, nullptr, "UnsetXpadAutoPilotState"},
- {60, nullptr, "DeactivateJoyXpad"},
+ {60, nullptr, "ClearNpadSystemCommonPolicy"},
+ {61, nullptr, "DeactivateNpad"},
+ {62, nullptr, "ForceDisconnectNpad"},
{91, nullptr, "DeactivateGesture"},
{110, nullptr, "DeactivateHomeButton"},
{111, nullptr, "SetHomeButtonAutoPilotState"},
@@ -900,6 +1339,15 @@ public:
{141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
{142, nullptr, "DeactivateSevenSixAxisSensor"},
{143, nullptr, "GetConsoleSixAxisSensorCountStates"},
+ {144, nullptr, "GetAccelerometerFsr"},
+ {145, nullptr, "SetAccelerometerFsr"},
+ {146, nullptr, "GetAccelerometerOdr"},
+ {147, nullptr, "SetAccelerometerOdr"},
+ {148, nullptr, "GetGyroscopeFsr"},
+ {149, nullptr, "SetGyroscopeFsr"},
+ {150, nullptr, "GetGyroscopeOdr"},
+ {151, nullptr, "SetGyroscopeOdr"},
+ {152, nullptr, "GetWhoAmI"},
{201, nullptr, "ActivateFirmwareUpdate"},
{202, nullptr, "DeactivateFirmwareUpdate"},
{203, nullptr, "StartFirmwareUpdate"},
@@ -928,6 +1376,17 @@ public:
{233, nullptr, "ClearPairingInfo"},
{234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
{235, nullptr, "EnableAnalogStickPower"},
+ {236, nullptr, "RequestKuinaUartClockCal"},
+ {237, nullptr, "GetKuinaUartClockCal"},
+ {238, nullptr, "SetKuinaUartClockTrim"},
+ {239, nullptr, "KuinaLoopbackTest"},
+ {240, nullptr, "RequestBatteryVoltage"},
+ {241, nullptr, "GetBatteryVoltage"},
+ {242, nullptr, "GetUniquePadPowerInfo"},
+ {243, nullptr, "RebootUniquePad"},
+ {244, nullptr, "RequestKuinaFirmwareVersion"},
+ {245, nullptr, "GetKuinaFirmwareVersion"},
+ {246, nullptr, "GetVidPid"},
{301, nullptr, "GetAbstractedPadHandles"},
{302, nullptr, "GetAbstractedPadState"},
{303, nullptr, "GetAbstractedPadsState"},
@@ -946,6 +1405,17 @@ public:
{350, nullptr, "AddRegisteredDevice"},
{400, nullptr, "DisableExternalMcuOnNxDevice"},
{401, nullptr, "DisableRailDeviceFiltering"},
+ {402, nullptr, "EnableWiredPairing"},
+ {403, nullptr, "EnableShipmentModeAutoClear"},
+ {500, nullptr, "SetFactoryInt"},
+ {501, nullptr, "IsFactoryBootEnabled"},
+ {550, nullptr, "SetAnalogStickModelDataTemporarily"},
+ {551, nullptr, "GetAnalogStickModelData"},
+ {552, nullptr, "ResetAnalogStickModelData"},
+ {600, nullptr, "ConvertPadState"},
+ {2000, nullptr, "DeactivateDigitizer"},
+ {2001, nullptr, "SetDigitizerAutoPilotState"},
+ {2002, nullptr, "UnsetDigitizerAutoPilotState"},
};
// clang-format on
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 039c38b58..c8e4a4b55 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,10 +4,9 @@
#pragma once
-#include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/service.h"
+#include <chrono>
-#include "controllers/controller_base.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/service.h"
namespace Core::Timing {
@@ -65,11 +64,13 @@ private:
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
- void UpdateControllers(u64 userdata, s64 cycles_late);
+ void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
std::shared_ptr<Kernel::SharedMemory> shared_mem;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
+ std::shared_ptr<Core::Timing::EventType> motion_update_event;
Core::System& system;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -85,16 +86,23 @@ public:
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
- void ActivateXpad(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
- void ActivateGesture(Kernel::HLERequestContext& ctx);
- void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
+ void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
+ void ActivateXpad(Kernel::HLERequestContext& ctx);
+ void GetXpadIDs(Kernel::HLERequestContext& ctx);
+ void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
+ void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
+ void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
+ void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
@@ -103,6 +111,7 @@ private:
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
+ void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
@@ -114,21 +123,45 @@ private:
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
- void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
- void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
+ void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
+ void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
- void SendVibrationValues(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
- void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
+ void SendVibrationValues(Kernel::HLERequestContext& ctx);
+ void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
- void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
- void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
+
+ enum class VibrationDeviceType : u32 {
+ LinearResonantActuator = 1,
+ };
+
+ enum class VibrationDevicePosition : u32 {
+ None = 0,
+ Left = 1,
+ Right = 2,
+ };
+
+ struct VibrationDeviceInfo {
+ VibrationDeviceType type{};
+ VibrationDevicePosition position{};
+ };
+ static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
std::shared_ptr<IAppletResource> applet_resource;
Core::System& system;
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 5e79e2c1a..e82fd031b 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -6,6 +6,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/irs.h"
@@ -38,9 +39,8 @@ IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) {
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- shared_mem = Kernel::SharedMemory::Create(
- kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
+
+ shared_mem = SharedFrom(&kernel.GetIrsSharedMem());
}
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u64>(system.CoreTiming().GetTicks());
+ rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
rb.PushRaw<u32>(0);
}
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index e8f9f2d29..17350b403 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -47,6 +47,7 @@ public:
{26, &LBL::EnableVrMode, "EnableVrMode"},
{27, &LBL::DisableVrMode, "DisableVrMode"},
{28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
+ {29, nullptr, "IsAutoBrightnessControlSupported"},
};
// clang-format on
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 92adde6d4..49972cd69 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -69,6 +69,7 @@ public:
{101, nullptr, "GetNetworkInfoLatestUpdate"},
{102, nullptr, "Scan"},
{103, nullptr, "ScanPrivate"},
+ {104, nullptr, "SetWirelessControllerRestriction"},
{200, nullptr, "OpenAccessPoint"},
{201, nullptr, "CloseAccessPoint"},
{202, nullptr, "CreateNetwork"},
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 647943020..65c209725 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -8,15 +8,23 @@
#include "common/alignment.h"
#include "common/hex_util.h"
+#include "common/scope_exit.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/memory/page_table.h"
+#include "core/hle/kernel/memory/system_control.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"
+#include "core/memory.h"
namespace Service::LDR {
-constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
+constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
+
+[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
@@ -26,10 +34,84 @@ constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
-constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
+[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
-constexpr u64 MAXIMUM_LOADED_RO = 0x40;
+constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
+constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200};
+
+constexpr std::size_t TEXT_INDEX{0};
+constexpr std::size_t RO_INDEX{1};
+constexpr std::size_t DATA_INDEX{2};
+
+struct NRRCertification {
+ u64_le application_id_mask;
+ u64_le application_id_pattern;
+ INSERT_PADDING_BYTES(0x10);
+ std::array<u8, 0x100> public_key; // Also known as modulus
+ std::array<u8, 0x100> signature;
+};
+static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size.");
+
+struct NRRHeader {
+ u32_le magic;
+ u32_le certification_signature_key_generation; // 9.0.0+
+ INSERT_PADDING_WORDS(2);
+ NRRCertification certification;
+ std::array<u8, 0x100> signature;
+ u64_le application_id;
+ u32_le size;
+ u8 nrr_kind; // 7.0.0+
+ INSERT_PADDING_BYTES(3);
+ u32_le hash_offset;
+ u32_le hash_count;
+ INSERT_PADDING_WORDS(2);
+};
+static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size.");
+
+struct SegmentHeader {
+ u32_le memory_offset;
+ u32_le memory_size;
+};
+static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size.");
+
+struct NROHeader {
+ // Switchbrew calls this "Start" (0x10)
+ INSERT_PADDING_WORDS(1);
+ u32_le mod_offset;
+ INSERT_PADDING_WORDS(2);
+
+ // Switchbrew calls this "Header" (0x70)
+ u32_le magic;
+ u32_le version;
+ u32_le nro_size;
+ u32_le flags;
+ // .text, .ro, .data
+ std::array<SegmentHeader, 3> segment_headers;
+ u32_le bss_size;
+ INSERT_PADDING_WORDS(1);
+ std::array<u8, 0x20> build_id;
+ u32_le dso_handle_offset;
+ INSERT_PADDING_WORDS(1);
+ // .apiInfo, .dynstr, .dynsym
+ std::array<SegmentHeader, 3> segment_headers_2;
+};
+static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
+
+using SHA256Hash = std::array<u8, 0x20>;
+
+struct NROInfo {
+ SHA256Hash hash{};
+ VAddr nro_address{};
+ std::size_t nro_size{};
+ VAddr bss_address{};
+ std::size_t bss_size{};
+ std::size_t text_size{};
+ std::size_t ro_size{};
+ std::size_t data_size{};
+ VAddr src_addr{};
+};
+static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size.");
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
@@ -55,6 +137,7 @@ public:
{1, nullptr, "GetProgramInfo"},
{2, nullptr, "RegisterTitle"},
{3, nullptr, "UnregisterTitle"},
+ {4, nullptr, "SetEnabledProgramVerification"},
};
// clang-format on
@@ -164,11 +247,11 @@ public:
return;
}
- if (system.CurrentProcess()->GetTitleID() != header.title_id) {
+ if (system.CurrentProcess()->GetTitleID() != header.application_id) {
LOG_ERROR(Service_LDR,
"Attempting to load NRR with title ID other than current process. (actual "
"{:016X})!",
- header.title_id);
+ header.application_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR);
return;
@@ -191,45 +274,140 @@ public:
}
void UnloadNrr(Kernel::HLERequestContext& ctx) {
- if (!initialized) {
- LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_NOT_INITIALIZED);
- return;
+ IPC::RequestParser rp{ctx};
+ const auto pid = rp.Pop<u64>();
+ const auto nrr_address = rp.Pop<VAddr>();
+
+ LOG_DEBUG(Service_LDR, "called with pid={}, nrr_address={:016X}", pid, nrr_address);
+
+ nrr.erase(nrr_address);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start,
+ std::size_t size) const {
+ constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize};
+ const auto start_info{page_table.QueryInfo(start - 1)};
+
+ if (start_info.state != Kernel::Memory::MemoryState::Free) {
+ return {};
}
- struct Parameters {
- u64_le process_id;
- u64_le nrr_address;
- };
+ if (start_info.GetAddress() > (start - padding_size)) {
+ return {};
+ }
- IPC::RequestParser rp{ctx};
- const auto [process_id, nrr_address] = rp.PopRaw<Parameters>();
+ const auto end_info{page_table.QueryInfo(start + size)};
- LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id,
- nrr_address);
+ if (end_info.state != Kernel::Memory::MemoryState::Free) {
+ return {};
+ }
- if (!Common::Is4KBAligned(nrr_address)) {
- LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
- nrr_address);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ALIGNMENT);
- return;
+ return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
+ }
+
+ VAddr GetRandomMapRegion(const Kernel::Memory::PageTable& page_table, std::size_t size) const {
+ VAddr addr{};
+ const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >>
+ Kernel::Memory::PageBits};
+ do {
+ addr = page_table.GetAliasCodeRegionStart() +
+ (Kernel::Memory::SystemControl::GenerateRandomRange(0, end_pages)
+ << Kernel::Memory::PageBits);
+ } while (!page_table.IsInsideAddressSpace(addr, size) ||
+ page_table.IsInsideHeapRegion(addr, size) ||
+ page_table.IsInsideAliasRegion(addr, size));
+ return addr;
+ }
+
+ ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress,
+ u64 size) const {
+ for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
+ auto& page_table{process->PageTable()};
+ const VAddr addr{GetRandomMapRegion(page_table, size)};
+ const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)};
+
+ if (result == Kernel::ERR_INVALID_ADDRESS_STATE) {
+ continue;
+ }
+
+ CASCADE_CODE(result);
+
+ if (ValidateRegionForMap(page_table, addr, size)) {
+ return MakeResult<VAddr>(addr);
+ }
}
- const auto iter = nrr.find(nrr_address);
- if (iter == nrr.end()) {
- LOG_ERROR(Service_LDR,
- "Attempting to unload NRR which has not been loaded! (addr={:016X})",
- nrr_address);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_NRR_ADDRESS);
- return;
+ return ERROR_INSUFFICIENT_ADDRESS_SPACE;
+ }
+
+ ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size,
+ VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
+ for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
+ auto& page_table{process->PageTable()};
+ VAddr addr{};
+
+ CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size));
+
+ if (bss_size) {
+ auto block_guard = detail::ScopeExit([&] {
+ page_table.UnmapProcessCodeMemory(addr + nro_size, bss_addr, bss_size);
+ page_table.UnmapProcessCodeMemory(addr, nro_addr, nro_size);
+ });
+
+ const ResultCode result{
+ page_table.MapProcessCodeMemory(addr + nro_size, bss_addr, bss_size)};
+
+ if (result == Kernel::ERR_INVALID_ADDRESS_STATE) {
+ continue;
+ }
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ block_guard.Cancel();
+ }
+
+ if (ValidateRegionForMap(page_table, addr, size)) {
+ return MakeResult<VAddr>(addr);
+ }
}
- nrr.erase(iter);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ return ERROR_INSUFFICIENT_ADDRESS_SPACE;
+ }
+
+ ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr,
+ VAddr start) const {
+ const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset};
+ const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset};
+ const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
+ const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size};
+ const VAddr bss_end_addr{
+ Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)};
+
+ auto CopyCode{[&](VAddr src_addr, VAddr dst_addr, u64 size) {
+ std::vector<u8> source_data(size);
+ system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size());
+ system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size());
+ }};
+ CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start,
+ nro_header.segment_headers[TEXT_INDEX].memory_size);
+ CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start,
+ nro_header.segment_headers[RO_INDEX].memory_size);
+ CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
+ nro_header.segment_headers[DATA_INDEX].memory_size);
+
+ CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
+ text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute));
+ CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
+ ro_start, data_start - ro_start, Kernel::Memory::MemoryPermission::Read));
+
+ return process->PageTable().SetCodeMemoryPermission(
+ data_start, bss_end_addr - data_start, Kernel::Memory::MemoryPermission::ReadAndWrite);
}
void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -317,9 +495,9 @@ public:
return;
}
- NROHeader header;
+ // Load and validate the NRO header
+ NROHeader header{};
std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
-
if (!IsValidNRO(header, nro_size, bss_size)) {
LOG_ERROR(Service_LDR, "NRO was invalid!");
IPC::ResponseBuilder rb{ctx, 2};
@@ -327,62 +505,50 @@ public:
return;
}
- // Load NRO as new executable module
- auto* process = system.CurrentProcess();
- auto& vm_manager = process->VMManager();
- auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
-
- if (!map_address.Succeeded() ||
- *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
-
- LOG_ERROR(Service_LDR,
- "General error while allocation memory or no available memory to allocate!");
+ // Map memory for the NRO
+ const auto map_result{MapNro(system.CurrentProcess(), nro_address, nro_size, bss_address,
+ bss_size, nro_size + bss_size)};
+ if (map_result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_MEMORY_STATE);
- return;
+ rb.Push(map_result.Code());
}
- // Mark text and read-only region as ModuleCode
- ASSERT(vm_manager
- .MirrorMemory(*map_address, nro_address, header.text_size + header.ro_size,
- Kernel::MemoryState::ModuleCode)
- .IsSuccess());
- // Mark read/write region as ModuleCodeData, which is necessary if this region is used for
- // TransferMemory (e.g. Final Fantasy VIII Remastered does this)
- ASSERT(vm_manager
- .MirrorMemory(*map_address + header.rw_offset, nro_address + header.rw_offset,
- header.rw_size, Kernel::MemoryState::ModuleCodeData)
- .IsSuccess());
- // Revoke permissions from the old memory region
- ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None)
- .IsSuccess());
-
- if (bss_size > 0) {
- // Mark BSS region as ModuleCodeData, which is necessary if this region is used for
- // TransferMemory (e.g. Final Fantasy VIII Remastered does this)
- ASSERT(vm_manager
- .MirrorMemory(*map_address + nro_size, bss_address, bss_size,
- Kernel::MemoryState::ModuleCodeData)
- .IsSuccess());
- ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None)
- .IsSuccess());
+ // Load the NRO into the mapped memory
+ if (const auto result{LoadNro(system.CurrentProcess(), header, nro_address, *map_result)};
+ result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(map_result.Code());
}
- vm_manager.ReprotectRange(*map_address, header.text_size,
- Kernel::VMAPermission::ReadExecute);
- vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
- Kernel::VMAPermission::Read);
- vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
- Kernel::VMAPermission::ReadWrite);
+ // Track the loaded NRO
+ nro.insert_or_assign(*map_result,
+ NROInfo{hash, *map_result, nro_size, bss_address, bss_size,
+ header.segment_headers[TEXT_INDEX].memory_size,
+ header.segment_headers[RO_INDEX].memory_size,
+ header.segment_headers[DATA_INDEX].memory_size, nro_address});
+ // Invalidate JIT caches for the newly mapped process code
system.InvalidateCpuInstructionCaches();
- nro.insert_or_assign(*map_address,
- NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
-
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(*map_address);
+ rb.Push(*map_result);
+ }
+
+ ResultCode UnmapNro(const NROInfo& info) {
+ // Each region must be unmapped separately to validate memory state
+ auto& page_table{system.CurrentProcess()->PageTable()};
+ CASCADE_CODE(page_table.UnmapProcessCodeMemory(info.nro_address + info.text_size +
+ info.ro_size + info.data_size,
+ info.bss_address, info.bss_size));
+ CASCADE_CODE(page_table.UnmapProcessCodeMemory(
+ info.nro_address + info.text_size + info.ro_size,
+ info.src_addr + info.text_size + info.ro_size, info.data_size));
+ CASCADE_CODE(page_table.UnmapProcessCodeMemory(
+ info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size));
+ CASCADE_CODE(
+ page_table.UnmapProcessCodeMemory(info.nro_address, info.src_addr, info.text_size));
+ return RESULT_SUCCESS;
}
void UnloadNro(Kernel::HLERequestContext& ctx) {
@@ -422,30 +588,15 @@ public:
return;
}
- auto& vm_manager = system.CurrentProcess()->VMManager();
- const auto& nro_info = iter->second;
-
- // Unmap the mirrored memory
- ASSERT(
- vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess());
-
- // Reprotect the source memory
- ASSERT(vm_manager
- .ReprotectRange(nro_info.nro_address, nro_info.nro_size,
- Kernel::VMAPermission::ReadWrite)
- .IsSuccess());
- if (nro_info.bss_size > 0) {
- ASSERT(vm_manager
- .ReprotectRange(nro_info.bss_address, nro_info.bss_size,
- Kernel::VMAPermission::ReadWrite)
- .IsSuccess());
- }
+ const auto result{UnmapNro(iter->second)};
system.InvalidateCpuInstructionCaches();
nro.erase(iter);
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+
+ rb.Push(result);
}
void Initialize(Kernel::HLERequestContext& ctx) {
@@ -458,56 +609,7 @@ public:
}
private:
- using SHA256Hash = std::array<u8, 0x20>;
-
- struct NROHeader {
- INSERT_PADDING_WORDS(1);
- u32_le mod_offset;
- INSERT_PADDING_WORDS(2);
- u32_le magic;
- u32_le version;
- u32_le nro_size;
- u32_le flags;
- u32_le text_offset;
- u32_le text_size;
- u32_le ro_offset;
- u32_le ro_size;
- u32_le rw_offset;
- u32_le rw_size;
- u32_le bss_size;
- INSERT_PADDING_WORDS(1);
- std::array<u8, 0x20> build_id;
- INSERT_PADDING_BYTES(0x20);
- };
- static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
-
- struct NRRHeader {
- u32_le magic;
- INSERT_PADDING_BYTES(12);
- u64_le title_id_mask;
- u64_le title_id_pattern;
- INSERT_PADDING_BYTES(16);
- std::array<u8, 0x100> modulus;
- std::array<u8, 0x100> signature_1;
- std::array<u8, 0x100> signature_2;
- u64_le title_id;
- u32_le size;
- INSERT_PADDING_BYTES(4);
- u32_le hash_offset;
- u32_le hash_count;
- INSERT_PADDING_BYTES(8);
- };
- static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
-
- struct NROInfo {
- SHA256Hash hash;
- VAddr nro_address;
- u64 nro_size;
- VAddr bss_address;
- u64 bss_size;
- };
-
- bool initialized = false;
+ bool initialized{};
std::map<VAddr, NROInfo> nro;
std::map<VAddr, std::vector<SHA256Hash>> nrr;
@@ -521,11 +623,21 @@ private:
static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
header.nro_size == nro_size && header.bss_size == bss_size &&
- header.ro_offset == header.text_offset + header.text_size &&
- header.rw_offset == header.ro_offset + header.ro_size &&
- nro_size == header.rw_offset + header.rw_size &&
- Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
- Common::Is4KBAligned(header.rw_size);
+
+ header.segment_headers[RO_INDEX].memory_offset ==
+ header.segment_headers[TEXT_INDEX].memory_offset +
+ header.segment_headers[TEXT_INDEX].memory_size &&
+
+ header.segment_headers[DATA_INDEX].memory_offset ==
+ header.segment_headers[RO_INDEX].memory_offset +
+ header.segment_headers[RO_INDEX].memory_size &&
+
+ nro_size == header.segment_headers[DATA_INDEX].memory_offset +
+ header.segment_headers[DATA_INDEX].memory_size &&
+
+ Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size) &&
+ Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size) &&
+ Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size);
}
Core::System& system;
};
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 346c8f899..49a42a9c9 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/lm/manager.h"
@@ -17,7 +18,7 @@ namespace Service::LM {
class ILogger final : public ServiceFramework<ILogger> {
public:
- explicit ILogger(Manager& manager_, Memory::Memory& memory_)
+ explicit ILogger(Manager& manager_, Core::Memory::Memory& memory_)
: ServiceFramework("ILogger"), manager{manager_}, memory{memory_} {
static const FunctionInfo functions[] = {
{0, &ILogger::Log, "Log"},
@@ -75,12 +76,12 @@ private:
}
Manager& manager;
- Memory::Memory& memory;
+ Core::Memory::Memory& memory;
};
class LM final : public ServiceFramework<LM> {
public:
- explicit LM(Manager& manager_, Memory::Memory& memory_)
+ explicit LM(Manager& manager_, Core::Memory::Memory& memory_)
: ServiceFramework{"lm"}, manager{manager_}, memory{memory_} {
// clang-format off
static const FunctionInfo functions[] = {
@@ -101,7 +102,7 @@ private:
}
Manager& manager;
- Memory::Memory& memory;
+ Core::Memory::Memory& memory;
};
void InstallInterfaces(Core::System& system) {
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp
index b67081b86..3ee2374e7 100644
--- a/src/core/hle/service/lm/manager.cpp
+++ b/src/core/hle/service/lm/manager.cpp
@@ -86,7 +86,8 @@ std::string FormatField(Field type, const std::vector<u8>& data) {
return Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(data.data()), data.size());
default:
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unimplemented field type={}", type);
+ return "";
}
}
diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp
index d16367f2c..113a4665c 100644
--- a/src/core/hle/service/mig/mig.cpp
+++ b/src/core/hle/service/mig/mig.cpp
@@ -20,6 +20,12 @@ public:
{101, nullptr, "ResumeServer"},
{200, nullptr, "CreateClient"},
{201, nullptr, "ResumeClient"},
+ {1001, nullptr, "Unknown1001"},
+ {1010, nullptr, "Unknown1010"},
+ {1100, nullptr, "Unknown1100"},
+ {1101, nullptr, "Unknown1101"},
+ {1200, nullptr, "Unknown1200"},
+ {1201, nullptr, "Unknown1201"}
};
// clang-format on
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp
new file mode 100644
index 000000000..d73b90015
--- /dev/null
+++ b/src/core/hle/service/mii/manager.cpp
@@ -0,0 +1,484 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <random>
+
+#include "common/assert.h"
+#include "common/file_util.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+
+#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/mii/manager.h"
+#include "core/hle/service/mii/raw_data.h"
+#include "core/hle/service/mii/types.h"
+
+namespace Service::Mii {
+
+namespace {
+
+constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
+
+constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
+
+constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
+constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
+constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13};
+constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23};
+constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0};
+constexpr std::array<u8, 62> EyeRotateLookup{
+ {0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
+ 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
+ 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}};
+constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07,
+ 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06,
+ 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}};
+
+template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
+std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
+ std::array<T, DestArraySize> out{};
+ std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
+ return out;
+}
+
+MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
+ MiiStoreBitFields bf;
+ std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
+
+ return {
+ .uuid = data.data.uuid,
+ .name = ResizeArray<char16_t, 10, 11>(data.data.name),
+ .font_region = static_cast<u8>(bf.font_region.Value()),
+ .favorite_color = static_cast<u8>(bf.favorite_color.Value()),
+ .gender = static_cast<u8>(bf.gender.Value()),
+ .height = static_cast<u8>(bf.height.Value()),
+ .build = static_cast<u8>(bf.build.Value()),
+ .type = static_cast<u8>(bf.type.Value()),
+ .region_move = static_cast<u8>(bf.region_move.Value()),
+ .faceline_type = static_cast<u8>(bf.faceline_type.Value()),
+ .faceline_color = static_cast<u8>(bf.faceline_color.Value()),
+ .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()),
+ .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()),
+ .hair_type = static_cast<u8>(bf.hair_type.Value()),
+ .hair_color = static_cast<u8>(bf.hair_color.Value()),
+ .hair_flip = static_cast<u8>(bf.hair_flip.Value()),
+ .eye_type = static_cast<u8>(bf.eye_type.Value()),
+ .eye_color = static_cast<u8>(bf.eye_color.Value()),
+ .eye_scale = static_cast<u8>(bf.eye_scale.Value()),
+ .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()),
+ .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()),
+ .eye_x = static_cast<u8>(bf.eye_x.Value()),
+ .eye_y = static_cast<u8>(bf.eye_y.Value()),
+ .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()),
+ .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()),
+ .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()),
+ .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()),
+ .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()),
+ .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()),
+ .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3),
+ .nose_type = static_cast<u8>(bf.nose_type.Value()),
+ .nose_scale = static_cast<u8>(bf.nose_scale.Value()),
+ .nose_y = static_cast<u8>(bf.nose_y.Value()),
+ .mouth_type = static_cast<u8>(bf.mouth_type.Value()),
+ .mouth_color = static_cast<u8>(bf.mouth_color.Value()),
+ .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()),
+ .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()),
+ .mouth_y = static_cast<u8>(bf.mouth_y.Value()),
+ .beard_color = static_cast<u8>(bf.beard_color.Value()),
+ .beard_type = static_cast<u8>(bf.beard_type.Value()),
+ .mustache_type = static_cast<u8>(bf.mustache_type.Value()),
+ .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()),
+ .mustache_y = static_cast<u8>(bf.mustache_y.Value()),
+ .glasses_type = static_cast<u8>(bf.glasses_type.Value()),
+ .glasses_color = static_cast<u8>(bf.glasses_color.Value()),
+ .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()),
+ .glasses_y = static_cast<u8>(bf.glasses_y.Value()),
+ .mole_type = static_cast<u8>(bf.mole_type.Value()),
+ .mole_scale = static_cast<u8>(bf.mole_scale.Value()),
+ .mole_x = static_cast<u8>(bf.mole_x.Value()),
+ .mole_y = static_cast<u8>(bf.mole_y.Value()),
+ };
+}
+
+u16 GenerateCrc16(const void* data, std::size_t size) {
+ s32 crc{};
+ for (std::size_t i = 0; i < size; i++) {
+ crc ^= static_cast<const u8*>(data)[i] << 8;
+ for (std::size_t j = 0; j < 8; j++) {
+ crc <<= 1;
+ if ((crc & 0x10000) != 0) {
+ crc = (crc ^ 0x1021) & 0xFFFF;
+ }
+ }
+ }
+ return Common::swap16(static_cast<u16>(crc));
+}
+
+Common::UUID GenerateValidUUID() {
+ auto uuid{Common::UUID::Generate()};
+
+ // Bit 7 must be set, and bit 6 unset for the UUID to be valid
+ uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL;
+ uuid.uuid[1] |= 0x0000000000000080ULL;
+
+ return uuid;
+}
+
+template <typename T>
+T GetRandomValue(T min, T max) {
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max));
+ return static_cast<T>(distribution(gen));
+}
+
+template <typename T>
+T GetRandomValue(T max) {
+ return GetRandomValue<T>({}, max);
+}
+
+template <typename T>
+T GetArrayValue(const u8* data, std::size_t index) {
+ T result{};
+ std::memcpy(&result, &data[index * sizeof(T)], sizeof(T));
+ return result;
+}
+
+MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
+ MiiStoreBitFields bf{};
+
+ if (gender == Gender::All) {
+ gender = GetRandomValue<Gender>(Gender::Maximum);
+ }
+
+ bf.gender.Assign(gender);
+ bf.favorite_color.Assign(GetRandomValue<u8>(11));
+ bf.region_move.Assign(0);
+ bf.font_region.Assign(FontRegion::Standard);
+ bf.type.Assign(0);
+ bf.height.Assign(64);
+ bf.build.Assign(64);
+
+ if (age == Age::All) {
+ const auto temp{GetRandomValue<int>(10)};
+ if (temp >= 8) {
+ age = Age::Old;
+ } else if (temp >= 4) {
+ age = Age::Normal;
+ } else {
+ age = Age::Young;
+ }
+ }
+
+ if (race == Race::All) {
+ const auto temp{GetRandomValue<int>(10)};
+ if (temp >= 8) {
+ race = Race::Black;
+ } else if (temp >= 4) {
+ race = Race::White;
+ } else {
+ race = Race::Asian;
+ }
+ }
+
+ u32 axis_y{};
+ if (gender == Gender::Female && age == Age::Young) {
+ axis_y = GetRandomValue<u32>(3);
+ }
+
+ const std::size_t index{3 * static_cast<std::size_t>(age) +
+ 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
+
+ const auto faceline_type_info{
+ GetArrayValue<RandomMiiData4>(&RawData::RandomMiiFaceline[0], index)};
+ const auto faceline_color_info{GetArrayValue<RandomMiiData3>(
+ RawData::RandomMiiFacelineColor.data(),
+ 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
+ const auto faceline_wrinkle_info{
+ GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineWrinkle.data(), index)};
+ const auto faceline_makeup_info{
+ GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineMakeup.data(), index)};
+ const auto hair_type_info{
+ GetArrayValue<RandomMiiData4>(RawData::RandomMiiHairType.data(), index)};
+ const auto hair_color_info{GetArrayValue<RandomMiiData3>(RawData::RandomMiiHairColor.data(),
+ 3 * static_cast<std::size_t>(race) +
+ static_cast<std::size_t>(age))};
+ const auto eye_type_info{
+ GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyeType.data(), index)};
+ const auto eye_color_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiEyeColor.data(),
+ static_cast<std::size_t>(race))};
+ const auto eyebrow_type_info{
+ GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyebrowType.data(), index)};
+ const auto nose_type_info{
+ GetArrayValue<RandomMiiData4>(RawData::RandomMiiNoseType.data(), index)};
+ const auto mouth_type_info{
+ GetArrayValue<RandomMiiData4>(RawData::RandomMiiMouthType.data(), index)};
+ const auto glasses_type_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiGlassType.data(),
+ static_cast<std::size_t>(age))};
+
+ bf.faceline_type.Assign(
+ faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
+ bf.faceline_color.Assign(
+ faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
+ bf.faceline_wrinkle.Assign(
+ faceline_wrinkle_info
+ .values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
+ bf.faceline_makeup.Assign(
+ faceline_makeup_info
+ .values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
+
+ bf.hair_type.Assign(
+ hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]);
+ bf.hair_color.Assign(
+ HairColorLookup[hair_color_info
+ .values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]);
+ bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum));
+
+ bf.eye_type.Assign(
+ eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]);
+
+ const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
+ const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
+ const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
+ const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]};
+
+ bf.eye_color.Assign(
+ EyeColorLookup[eye_color_info
+ .values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]);
+ bf.eye_scale.Assign(4);
+ bf.eye_aspect.Assign(3);
+ bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate);
+ bf.eye_x.Assign(2);
+ bf.eye_y.Assign(axis_y + 12);
+
+ bf.eyebrow_type.Assign(
+ eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
+
+ const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
+ const auto eyebrow_y{race == Race::Asian ? 9 : 10};
+ const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6};
+ const auto eyebrow_rotate{
+ 32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]};
+
+ bf.eyebrow_color.Assign(bf.hair_color);
+ bf.eyebrow_scale.Assign(4);
+ bf.eyebrow_aspect.Assign(3);
+ bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate);
+ bf.eyebrow_x.Assign(2);
+ bf.eyebrow_y.Assign(axis_y + eyebrow_y);
+
+ const auto nose_scale{gender == Gender::Female ? 3 : 4};
+
+ bf.nose_type.Assign(
+ nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]);
+ bf.nose_scale.Assign(nose_scale);
+ bf.nose_y.Assign(axis_y + 9);
+
+ const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0};
+
+ bf.mouth_type.Assign(
+ mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
+ bf.mouth_color.Assign(MouthColorLookup[mouth_color]);
+ bf.mouth_scale.Assign(4);
+ bf.mouth_aspect.Assign(3);
+ bf.mouth_y.Assign(axis_y + 13);
+
+ bf.beard_color.Assign(bf.hair_color);
+ bf.mustache_scale.Assign(4);
+
+ if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) {
+ const auto mustache_and_beard_flag{
+ GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)};
+
+ auto beard_type{BeardType::None};
+ auto mustache_type{MustacheType::None};
+
+ if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
+ BeardAndMustacheFlag::Beard) {
+ beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5);
+ }
+
+ if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
+ BeardAndMustacheFlag::Mustache) {
+ mustache_type =
+ GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5);
+ }
+
+ bf.mustache_type.Assign(mustache_type);
+ bf.beard_type.Assign(beard_type);
+ bf.mustache_y.Assign(10);
+ } else {
+ bf.mustache_type.Assign(MustacheType::None);
+ bf.beard_type.Assign(BeardType::None);
+ bf.mustache_y.Assign(axis_y + 10);
+ }
+
+ const auto glasses_type_start{GetRandomValue<std::size_t>(100)};
+ u8 glasses_type{};
+ while (glasses_type_start < glasses_type_info.values[glasses_type]) {
+ if (++glasses_type >= glasses_type_info.values_count) {
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ bf.glasses_type.Assign(glasses_type);
+ bf.glasses_color.Assign(GlassesColorLookup[0]);
+ bf.glasses_scale.Assign(4);
+ bf.glasses_y.Assign(axis_y + 10);
+
+ bf.mole_type.Assign(0);
+ bf.mole_scale.Assign(4);
+ bf.mole_x.Assign(2);
+ bf.mole_y.Assign(20);
+
+ return {DefaultMiiName, bf, user_id};
+}
+
+MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
+ MiiStoreBitFields bf{};
+
+ bf.font_region.Assign(info.font_region);
+ bf.favorite_color.Assign(info.favorite_color);
+ bf.gender.Assign(info.gender);
+ bf.height.Assign(info.height);
+ bf.build.Assign(info.weight);
+ bf.type.Assign(info.type);
+ bf.region_move.Assign(info.region);
+ bf.faceline_type.Assign(info.face_type);
+ bf.faceline_color.Assign(info.face_color);
+ bf.faceline_wrinkle.Assign(info.face_wrinkle);
+ bf.faceline_makeup.Assign(info.face_makeup);
+ bf.hair_type.Assign(info.hair_type);
+ bf.hair_color.Assign(HairColorLookup[info.hair_color]);
+ bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip));
+ bf.eye_type.Assign(info.eye_type);
+ bf.eye_color.Assign(EyeColorLookup[info.eye_color]);
+ bf.eye_scale.Assign(info.eye_scale);
+ bf.eye_aspect.Assign(info.eye_aspect);
+ bf.eye_rotate.Assign(info.eye_rotate);
+ bf.eye_x.Assign(info.eye_x);
+ bf.eye_y.Assign(info.eye_y);
+ bf.eyebrow_type.Assign(info.eyebrow_type);
+ bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]);
+ bf.eyebrow_scale.Assign(info.eyebrow_scale);
+ bf.eyebrow_aspect.Assign(info.eyebrow_aspect);
+ bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
+ bf.eyebrow_x.Assign(info.eyebrow_x);
+ bf.eyebrow_y.Assign(info.eyebrow_y - 3);
+ bf.nose_type.Assign(info.nose_type);
+ bf.nose_scale.Assign(info.nose_scale);
+ bf.nose_y.Assign(info.nose_y);
+ bf.mouth_type.Assign(info.mouth_type);
+ bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]);
+ bf.mouth_scale.Assign(info.mouth_scale);
+ bf.mouth_aspect.Assign(info.mouth_aspect);
+ bf.mouth_y.Assign(info.mouth_y);
+ bf.beard_color.Assign(HairColorLookup[info.beard_color]);
+ bf.beard_type.Assign(static_cast<BeardType>(info.beard_type));
+ bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type));
+ bf.mustache_scale.Assign(info.mustache_scale);
+ bf.mustache_y.Assign(info.mustache_y);
+ bf.glasses_type.Assign(info.glasses_type);
+ bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]);
+ bf.glasses_scale.Assign(info.glasses_scale);
+ bf.glasses_y.Assign(info.glasses_y);
+ bf.mole_type.Assign(info.mole_type);
+ bf.mole_scale.Assign(info.mole_scale);
+ bf.mole_x.Assign(info.mole_x);
+ bf.mole_y.Assign(info.mole_y);
+
+ return {DefaultMiiName, bf, user_id};
+}
+
+} // namespace
+
+MiiStoreData::MiiStoreData() = default;
+
+MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
+ const Common::UUID& user_id) {
+ data.name = name;
+ data.uuid = GenerateValidUUID();
+
+ std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
+ data_crc = GenerateCrc16(data.data.data(), sizeof(data));
+ device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID));
+}
+
+MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {}
+
+bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
+ if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+ return false;
+ }
+
+ const bool result{current_update_counter != update_counter};
+
+ current_update_counter = update_counter;
+
+ return result;
+}
+
+bool MiiManager::IsFullDatabase() const {
+ // TODO(bunnei): We don't implement the Mii database, so it cannot be full
+ return false;
+}
+
+u32 MiiManager::GetCount(SourceFlag source_flag) const {
+ std::size_t count{};
+ if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
+ // TODO(bunnei): We don't implement the Mii database, but when we do, update this
+ count += 0;
+ }
+ if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
+ count += DefaultMiiCount;
+ }
+ return static_cast<u32>(count);
+}
+
+ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info,
+ SourceFlag source_flag) {
+ if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+ return ERROR_CANNOT_FIND_ENTRY;
+ }
+
+ // TODO(bunnei): We don't implement the Mii database, so we can't have an entry
+ return ERROR_CANNOT_FIND_ENTRY;
+}
+
+MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
+ return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
+}
+
+MiiInfo MiiManager::BuildDefault(std::size_t index) {
+ return ConvertStoreDataToInfo(BuildDefaultStoreData(
+ GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
+}
+
+ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
+ std::vector<MiiInfoElement> result;
+
+ if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
+ return MakeResult(std::move(result));
+ }
+
+ for (std::size_t index = 0; index < DefaultMiiCount; index++) {
+ result.emplace_back(BuildDefault(index), Source::Default);
+ }
+
+ return MakeResult(std::move(result));
+}
+
+ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
+ constexpr u32 INVALID_INDEX{0xFFFFFFFF};
+
+ index = INVALID_INDEX;
+
+ // TODO(bunnei): We don't implement the Mii database, so we can't have an index
+ return ERROR_CANNOT_FIND_ENTRY;
+}
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/manager.h b/src/core/hle/service/mii/manager.h
new file mode 100644
index 000000000..927451dea
--- /dev/null
+++ b/src/core/hle/service/mii/manager.h
@@ -0,0 +1,331 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/uuid.h"
+#include "core/hle/result.h"
+#include "core/hle/service/mii/types.h"
+
+namespace Service::Mii {
+
+enum class Source : u32 {
+ Database = 0,
+ Default = 1,
+ Account = 2,
+ Friend = 3,
+};
+
+enum class SourceFlag : u32 {
+ None = 0,
+ Database = 1 << 0,
+ Default = 1 << 1,
+};
+DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
+
+struct MiiInfo {
+ Common::UUID uuid{Common::INVALID_UUID};
+ std::array<char16_t, 11> name{};
+ u8 font_region{};
+ u8 favorite_color{};
+ u8 gender{};
+ u8 height{};
+ u8 build{};
+ u8 type{};
+ u8 region_move{};
+ u8 faceline_type{};
+ u8 faceline_color{};
+ u8 faceline_wrinkle{};
+ u8 faceline_make{};
+ u8 hair_type{};
+ u8 hair_color{};
+ u8 hair_flip{};
+ u8 eye_type{};
+ u8 eye_color{};
+ u8 eye_scale{};
+ u8 eye_aspect{};
+ u8 eye_rotate{};
+ u8 eye_x{};
+ u8 eye_y{};
+ u8 eyebrow_type{};
+ u8 eyebrow_color{};
+ u8 eyebrow_scale{};
+ u8 eyebrow_aspect{};
+ u8 eyebrow_rotate{};
+ u8 eyebrow_x{};
+ u8 eyebrow_y{};
+ u8 nose_type{};
+ u8 nose_scale{};
+ u8 nose_y{};
+ u8 mouth_type{};
+ u8 mouth_color{};
+ u8 mouth_scale{};
+ u8 mouth_aspect{};
+ u8 mouth_y{};
+ u8 beard_color{};
+ u8 beard_type{};
+ u8 mustache_type{};
+ u8 mustache_scale{};
+ u8 mustache_y{};
+ u8 glasses_type{};
+ u8 glasses_color{};
+ u8 glasses_scale{};
+ u8 glasses_y{};
+ u8 mole_type{};
+ u8 mole_scale{};
+ u8 mole_x{};
+ u8 mole_y{};
+ INSERT_PADDING_BYTES(1);
+
+ std::u16string Name() const;
+};
+static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
+static_assert(std::has_unique_object_representations_v<MiiInfo>,
+ "All bits of MiiInfo must contribute to its value.");
+
+#pragma pack(push, 4)
+
+struct MiiInfoElement {
+ MiiInfoElement(const MiiInfo& info, Source source) : info{info}, source{source} {}
+
+ MiiInfo info{};
+ Source source{};
+};
+static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
+
+struct MiiStoreBitFields {
+ union {
+ u32 word_0{};
+
+ BitField<0, 8, u32> hair_type;
+ BitField<8, 7, u32> height;
+ BitField<15, 1, u32> mole_type;
+ BitField<16, 7, u32> build;
+ BitField<23, 1, HairFlip> hair_flip;
+ BitField<24, 7, u32> hair_color;
+ BitField<31, 1, u32> type;
+ };
+
+ union {
+ u32 word_1{};
+
+ BitField<0, 7, u32> eye_color;
+ BitField<7, 1, Gender> gender;
+ BitField<8, 7, u32> eyebrow_color;
+ BitField<16, 7, u32> mouth_color;
+ BitField<24, 7, u32> beard_color;
+ };
+
+ union {
+ u32 word_2{};
+
+ BitField<0, 7, u32> glasses_color;
+ BitField<8, 6, u32> eye_type;
+ BitField<14, 2, u32> region_move;
+ BitField<16, 6, u32> mouth_type;
+ BitField<22, 2, FontRegion> font_region;
+ BitField<24, 5, u32> eye_y;
+ BitField<29, 3, u32> glasses_scale;
+ };
+
+ union {
+ u32 word_3{};
+
+ BitField<0, 5, u32> eyebrow_type;
+ BitField<5, 3, MustacheType> mustache_type;
+ BitField<8, 5, u32> nose_type;
+ BitField<13, 3, BeardType> beard_type;
+ BitField<16, 5, u32> nose_y;
+ BitField<21, 3, u32> mouth_aspect;
+ BitField<24, 5, u32> mouth_y;
+ BitField<29, 3, u32> eyebrow_aspect;
+ };
+
+ union {
+ u32 word_4{};
+
+ BitField<0, 5, u32> mustache_y;
+ BitField<5, 3, u32> eye_rotate;
+ BitField<8, 5, u32> glasses_y;
+ BitField<13, 3, u32> eye_aspect;
+ BitField<16, 5, u32> mole_x;
+ BitField<21, 3, u32> eye_scale;
+ BitField<24, 5, u32> mole_y;
+ };
+
+ union {
+ u32 word_5{};
+
+ BitField<0, 5, u32> glasses_type;
+ BitField<8, 4, u32> favorite_color;
+ BitField<12, 4, u32> faceline_type;
+ BitField<16, 4, u32> faceline_color;
+ BitField<20, 4, u32> faceline_wrinkle;
+ BitField<24, 4, u32> faceline_makeup;
+ BitField<28, 4, u32> eye_x;
+ };
+
+ union {
+ u32 word_6{};
+
+ BitField<0, 4, u32> eyebrow_scale;
+ BitField<4, 4, u32> eyebrow_rotate;
+ BitField<8, 4, u32> eyebrow_x;
+ BitField<12, 4, u32> eyebrow_y;
+ BitField<16, 4, u32> nose_scale;
+ BitField<20, 4, u32> mouth_scale;
+ BitField<24, 4, u32> mustache_scale;
+ BitField<28, 4, u32> mole_scale;
+ };
+};
+static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
+static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
+ "MiiStoreBitFields is not trivially copyable.");
+
+struct MiiStoreData {
+ using Name = std::array<char16_t, 10>;
+
+ MiiStoreData();
+ MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
+ const Common::UUID& user_id);
+
+ // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
+ // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
+ // not suitable for our uses.
+ struct {
+ std::array<u8, 0x1C> data{};
+ static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
+
+ Name name{};
+ Common::UUID uuid{Common::INVALID_UUID};
+ } data;
+
+ u16 data_crc{};
+ u16 device_crc{};
+};
+static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
+
+struct MiiStoreDataElement {
+ MiiStoreData data{};
+ Source source{};
+};
+static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
+
+struct MiiDatabase {
+ u32 magic{}; // 'NFDB'
+ std::array<MiiStoreData, 0x64> miis{};
+ INSERT_PADDING_BYTES(1);
+ u8 count{};
+ u16 crc{};
+};
+static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
+
+struct RandomMiiValues {
+ std::array<u8, 0xbc> values{};
+};
+static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
+
+struct RandomMiiData4 {
+ Gender gender{};
+ Age age{};
+ Race race{};
+ u32 values_count{};
+ std::array<u8, 0xbc> values{};
+};
+static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
+
+struct RandomMiiData3 {
+ u32 arg_1;
+ u32 arg_2;
+ u32 values_count;
+ std::array<u8, 0xbc> values{};
+};
+static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
+
+struct RandomMiiData2 {
+ u32 arg_1;
+ u32 values_count;
+ std::array<u8, 0xbc> values{};
+};
+static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
+
+struct DefaultMii {
+ u32 face_type{};
+ u32 face_color{};
+ u32 face_wrinkle{};
+ u32 face_makeup{};
+ u32 hair_type{};
+ u32 hair_color{};
+ u32 hair_flip{};
+ u32 eye_type{};
+ u32 eye_color{};
+ u32 eye_scale{};
+ u32 eye_aspect{};
+ u32 eye_rotate{};
+ u32 eye_x{};
+ u32 eye_y{};
+ u32 eyebrow_type{};
+ u32 eyebrow_color{};
+ u32 eyebrow_scale{};
+ u32 eyebrow_aspect{};
+ u32 eyebrow_rotate{};
+ u32 eyebrow_x{};
+ u32 eyebrow_y{};
+ u32 nose_type{};
+ u32 nose_scale{};
+ u32 nose_y{};
+ u32 mouth_type{};
+ u32 mouth_color{};
+ u32 mouth_scale{};
+ u32 mouth_aspect{};
+ u32 mouth_y{};
+ u32 mustache_type{};
+ u32 beard_type{};
+ u32 beard_color{};
+ u32 mustache_scale{};
+ u32 mustache_y{};
+ u32 glasses_type{};
+ u32 glasses_color{};
+ u32 glasses_scale{};
+ u32 glasses_y{};
+ u32 mole_type{};
+ u32 mole_scale{};
+ u32 mole_x{};
+ u32 mole_y{};
+ u32 height{};
+ u32 weight{};
+ Gender gender{};
+ u32 favorite_color{};
+ u32 region{};
+ FontRegion font_region{};
+ u32 type{};
+ INSERT_PADDING_WORDS(5);
+};
+static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
+
+#pragma pack(pop)
+
+// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
+// with providing an easy interface for HLE emulation of the mii service.
+class MiiManager {
+public:
+ MiiManager();
+
+ bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
+ bool IsFullDatabase() const;
+ u32 GetCount(SourceFlag source_flag) const;
+ ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag);
+ MiiInfo BuildRandom(Age age, Gender gender, Race race);
+ MiiInfo BuildDefault(std::size_t index);
+ ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
+ ResultCode GetIndex(const MiiInfo& info, u32& index);
+
+private:
+ const Common::UUID user_id;
+ u64 update_counter{};
+};
+
+}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index a128edb43..d7080b715 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -4,22 +4,17 @@
#include <memory>
-#include <fmt/ostream.h>
-
#include "common/logging/log.h"
-#include "common/string_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/service/mii/manager.h"
#include "core/hle/service/mii/mii.h"
-#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Mii {
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
-constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
-constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
@@ -31,19 +26,19 @@ public:
{2, &IDatabaseService::GetCount, "GetCount"},
{3, &IDatabaseService::Get, "Get"},
{4, &IDatabaseService::Get1, "Get1"},
- {5, nullptr, "UpdateLatest"},
+ {5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
- {8, &IDatabaseService::Get2, "Get2"},
- {9, &IDatabaseService::Get3, "Get3"},
+ {8, nullptr, "Get2"},
+ {9, nullptr, "Get3"},
{10, nullptr, "UpdateLatest1"},
- {11, &IDatabaseService::FindIndex, "FindIndex"},
- {12, &IDatabaseService::Move, "Move"},
- {13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
- {14, &IDatabaseService::Delete, "Delete"},
- {15, &IDatabaseService::DestroyFile, "DestroyFile"},
- {16, &IDatabaseService::DeleteFile, "DeleteFile"},
- {17, &IDatabaseService::Format, "Format"},
+ {11, nullptr, "FindIndex"},
+ {12, nullptr, "Move"},
+ {13, nullptr, "AddOrReplace"},
+ {14, nullptr, "Delete"},
+ {15, nullptr, "DestroyFile"},
+ {16, nullptr, "DeleteFile"},
+ {17, nullptr, "Format"},
{18, nullptr, "Import"},
{19, nullptr, "Export"},
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
@@ -52,6 +47,7 @@ public:
{23, nullptr, "Convert"},
{24, nullptr, "ConvertCoreDataToCharInfo"},
{25, nullptr, "ConvertCharInfoToCoreData"},
+ {26, nullptr, "Append"},
};
// clang-format on
@@ -59,31 +55,26 @@ public:
}
private:
- template <typename OutType>
- std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
- u32 requested_size, u32& read_size) {
- read_size = std::min(requested_size, db.Size() - offset);
-
- std::vector<u8> out(read_size * sizeof(OutType));
-
- for (u32 i = 0; i < read_size; ++i) {
- const auto obj = (db.*getter)(offset + i);
- std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
+ template <typename T>
+ std::vector<u8> SerializeArray(const std::vector<T>& values) {
+ std::vector<u8> out(values.size() * sizeof(T));
+ std::size_t offset{};
+ for (const auto& value : values) {
+ std::memcpy(out.data() + offset, &value, sizeof(T));
+ offset += sizeof(T);
}
-
return out;
}
void IsUpdated(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto source{rp.PopRaw<Source>()};
+ const auto source_flag{rp.PopRaw<SourceFlag>()};
- LOG_DEBUG(Service_Mii, "called with source={}", source);
+ LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(db.CheckUpdatedFlag());
- db.ResetUpdatedFlag();
+ rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
}
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
@@ -91,264 +82,149 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(db.Full());
+ rb.Push(manager.IsFullDatabase());
}
void GetCount(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto source{rp.PopRaw<Source>()};
+ const auto source_flag{rp.PopRaw<SourceFlag>()};
- LOG_DEBUG(Service_Mii, "called with source={}", source);
+ LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(db.Size());
+ rb.Push<u32>(manager.GetCount(source_flag));
}
- // Gets Miis from database at offset and index in format MiiInfoElement
void Get(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto size{rp.PopRaw<u32>()};
- const auto source{rp.PopRaw<Source>()};
-
- LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
- offsets[0], source);
-
- u32 read_size{};
- ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
- offsets[0] += read_size;
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(read_size);
- }
+ const auto source_flag{rp.PopRaw<SourceFlag>()};
- // Gets Miis from database at offset and index in format MiiInfo
- void Get1(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto size{rp.PopRaw<u32>()};
- const auto source{rp.PopRaw<Source>()};
+ LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
- LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
- offsets[1], source);
+ const auto result{manager.GetDefault(source_flag)};
+ if (result.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result.Code());
+ return;
+ }
- u32 read_size{};
- ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
- offsets[1] += read_size;
+ if (result->size() > 0) {
+ ctx.WriteBuffer(SerializeArray(*result));
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(read_size);
+ rb.Push<u32>(static_cast<u32>(result->size()));
}
- void BuildRandom(Kernel::HLERequestContext& ctx) {
+ void Get1(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
+ const auto source_flag{rp.PopRaw<SourceFlag>()};
- if (unknown1 > 3) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
- LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
- return;
- }
+ LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
- if (unknown2 > 2) {
+ const auto result{manager.GetDefault(source_flag)};
+ if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
- LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
+ rb.Push(result.Code());
return;
}
- if (unknown3 > 3) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
- LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
- return;
+ std::vector<MiiInfo> values;
+ for (const auto& element : *result) {
+ values.emplace_back(element.info);
}
- LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
- unknown1, unknown2, unknown3);
+ ctx.WriteBuffer(SerializeArray(values));
- const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<MiiInfo>(info);
+ rb.Push<u32>(static_cast<u32>(result->size()));
}
- void BuildDefault(Kernel::HLERequestContext& ctx) {
+ void UpdateLatest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto index{rp.PopRaw<u32>()};
+ const auto info{rp.PopRaw<MiiInfo>()};
+ const auto source_flag{rp.PopRaw<SourceFlag>()};
- if (index > 5) {
- LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
- index);
+ LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
+
+ const auto result{manager.UpdateLatest(info, source_flag)};
+ if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
+ rb.Push(result.Code());
return;
}
- LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
-
- const auto info = db.CreateDefault(index);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<MiiInfo>(info);
+ rb.PushRaw<MiiInfo>(*result);
}
- // Gets Miis from database at offset and index in format MiiStoreDataElement
- void Get2(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto size{rp.PopRaw<u32>()};
- const auto source{rp.PopRaw<Source>()};
-
- LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
- offsets[2], source);
-
- u32 read_size{};
- ctx.WriteBuffer(
- SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
- offsets[2] += read_size;
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(read_size);
- }
-
- // Gets Miis from database at offset and index in format MiiStoreData
- void Get3(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto size{rp.PopRaw<u32>()};
- const auto source{rp.PopRaw<Source>()};
-
- LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
- offsets[3], source);
-
- u32 read_size{};
- ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
- offsets[3] += read_size;
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(read_size);
- }
-
- void FindIndex(Kernel::HLERequestContext& ctx) {
+ void BuildRandom(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto uuid{rp.PopRaw<Common::UUID>()};
- const auto unknown{rp.PopRaw<bool>()};
- LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
+ const auto age{rp.PopRaw<Age>()};
+ const auto gender{rp.PopRaw<Gender>()};
+ const auto race{rp.PopRaw<Race>()};
- IPC::ResponseBuilder rb{ctx, 3};
+ LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race);
- const auto index = db.IndexOf(uuid);
- if (index > MAX_MIIS) {
- // TODO(DarkLordZach): Find a better error code
- rb.Push(RESULT_UNKNOWN);
- rb.Push(index);
- } else {
- rb.Push(RESULT_SUCCESS);
- rb.Push(index);
+ if (age > Age::All) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "invalid age={}", age);
+ return;
}
- }
- void Move(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto uuid{rp.PopRaw<Common::UUID>()};
- const auto index{rp.PopRaw<s32>()};
-
- if (index < 0) {
- LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
+ if (gender > Gender::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "invalid gender={}", gender);
return;
}
- LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
-
- const auto success = db.Move(uuid, index);
-
- IPC::ResponseBuilder rb{ctx, 2};
- // TODO(DarkLordZach): Find a better error code
- rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
- }
-
- void AddOrReplace(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto data{rp.PopRaw<MiiStoreData>()};
-
- LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
- Common::UTF16ToUTF8(data.Name()));
-
- const auto success = db.AddOrReplace(data);
-
- IPC::ResponseBuilder rb{ctx, 2};
- // TODO(DarkLordZach): Find a better error code
- rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
- }
-
- void Delete(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto uuid{rp.PopRaw<Common::UUID>()};
-
- LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
-
- const auto success = db.Remove(uuid);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
- }
-
- void DestroyFile(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Mii, "called");
-
- if (!db.IsTestModeEnabled()) {
- LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
+ if (race > Race::All) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_NOT_IN_TEST_MODE);
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "invalid race={}", race);
return;
}
- IPC::ResponseBuilder rb{ctx, 3};
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
- rb.Push(db.DestroyFile());
+ rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race));
}
- void DeleteFile(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Mii, "called");
+ void BuildDefault(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto index{rp.Pop<u32>()};
+
+ LOG_DEBUG(Service_Mii, "called with index={}", index);
- if (!db.IsTestModeEnabled()) {
- LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
+ if (index > 5) {
+ LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
+ index);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_NOT_IN_TEST_MODE);
+ rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push(db.DeleteFile());
- }
-
- void Format(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Mii, "called");
-
- db.Clear();
-
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<MiiInfo>(manager.BuildDefault(index));
}
void GetIndex(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<MiiInfo>()};
- LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
- Common::UTF16ToUTF8(info.Name()));
-
- const auto index = db.IndexOf(info);
+ LOG_DEBUG(Service_Mii, "called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ u32 index{};
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(manager.GetIndex(info, index));
rb.Push(index);
}
@@ -364,12 +240,14 @@ private:
rb.Push(RESULT_SUCCESS);
}
- MiiManager db;
+ constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
+ return current_interface_version >= interface_version;
+ }
- u32 current_interface_version = 0;
+ MiiManager manager;
- // Last read offsets of Get functions
- std::array<u32, 4> offsets{};
+ u32 current_interface_version{};
+ u64 current_update_counter{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
deleted file mode 100644
index 8d0353075..000000000
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cstring>
-#include "common/assert.h"
-#include "common/file_util.h"
-#include "common/logging/log.h"
-#include "common/string_util.h"
-#include "core/hle/service/mii/mii_manager.h"
-
-namespace Service::Mii {
-
-namespace {
-
-constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
-constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
-
-// This value was retrieved from HW test
-constexpr MiiStoreData DEFAULT_MII = {
- {
- 0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
- 0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
- },
- {'y', 'u', 'z', 'u', '\0'},
- Common::UUID{1, 0},
- 0,
- 0,
-};
-
-// Default values taken from multiple real databases
-const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
-
-constexpr std::array<const char*, 4> SOURCE_NAMES{
- "Database",
- "Default",
- "Account",
- "Friend",
-};
-
-template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
-std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
- std::array<T, DestArraySize> out{};
- std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
- return out;
-}
-
-MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
- MiiStoreBitFields bf{};
- std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
- return {
- data.uuid,
- ResizeArray<char16_t, 10, 11>(data.name),
- static_cast<u8>(bf.font_region.Value()),
- static_cast<u8>(bf.favorite_color.Value()),
- static_cast<u8>(bf.gender.Value()),
- static_cast<u8>(bf.height.Value()),
- static_cast<u8>(bf.weight.Value()),
- static_cast<u8>(bf.mii_type.Value()),
- static_cast<u8>(bf.mii_region.Value()),
- static_cast<u8>(bf.face_type.Value()),
- static_cast<u8>(bf.face_color.Value()),
- static_cast<u8>(bf.face_wrinkle.Value()),
- static_cast<u8>(bf.face_makeup.Value()),
- static_cast<u8>(bf.hair_type.Value()),
- static_cast<u8>(bf.hair_color.Value()),
- static_cast<bool>(bf.hair_flip.Value()),
- static_cast<u8>(bf.eye_type.Value()),
- static_cast<u8>(bf.eye_color.Value()),
- static_cast<u8>(bf.eye_scale.Value()),
- static_cast<u8>(bf.eye_aspect.Value()),
- static_cast<u8>(bf.eye_rotate.Value()),
- static_cast<u8>(bf.eye_x.Value()),
- static_cast<u8>(bf.eye_y.Value()),
- static_cast<u8>(bf.eyebrow_type.Value()),
- static_cast<u8>(bf.eyebrow_color.Value()),
- static_cast<u8>(bf.eyebrow_scale.Value()),
- static_cast<u8>(bf.eyebrow_aspect.Value()),
- static_cast<u8>(bf.eyebrow_rotate.Value()),
- static_cast<u8>(bf.eyebrow_x.Value()),
- static_cast<u8>(bf.eyebrow_y.Value()),
- static_cast<u8>(bf.nose_type.Value()),
- static_cast<u8>(bf.nose_scale.Value()),
- static_cast<u8>(bf.nose_y.Value()),
- static_cast<u8>(bf.mouth_type.Value()),
- static_cast<u8>(bf.mouth_color.Value()),
- static_cast<u8>(bf.mouth_scale.Value()),
- static_cast<u8>(bf.mouth_aspect.Value()),
- static_cast<u8>(bf.mouth_y.Value()),
- static_cast<u8>(bf.facial_hair_color.Value()),
- static_cast<u8>(bf.beard_type.Value()),
- static_cast<u8>(bf.mustache_type.Value()),
- static_cast<u8>(bf.mustache_scale.Value()),
- static_cast<u8>(bf.mustache_y.Value()),
- static_cast<u8>(bf.glasses_type.Value()),
- static_cast<u8>(bf.glasses_color.Value()),
- static_cast<u8>(bf.glasses_scale.Value()),
- static_cast<u8>(bf.glasses_y.Value()),
- static_cast<u8>(bf.mole_type.Value()),
- static_cast<u8>(bf.mole_scale.Value()),
- static_cast<u8>(bf.mole_x.Value()),
- static_cast<u8>(bf.mole_y.Value()),
- 0x00,
- };
-}
-MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
- MiiStoreData out{};
- out.name = ResizeArray<char16_t, 11, 10>(info.name);
- out.uuid = info.uuid;
-
- MiiStoreBitFields bf{};
-
- bf.hair_type.Assign(info.hair_type);
- bf.mole_type.Assign(info.mole_type);
- bf.height.Assign(info.height);
- bf.hair_flip.Assign(info.hair_flip);
- bf.weight.Assign(info.weight);
- bf.hair_color.Assign(info.hair_color);
-
- bf.gender.Assign(info.gender);
- bf.eye_color.Assign(info.eye_color);
- bf.eyebrow_color.Assign(info.eyebrow_color);
- bf.mouth_color.Assign(info.mouth_color);
- bf.facial_hair_color.Assign(info.facial_hair_color);
-
- bf.mii_type.Assign(info.mii_type);
- bf.glasses_color.Assign(info.glasses_color);
- bf.font_region.Assign(info.font_region);
- bf.eye_type.Assign(info.eye_type);
- bf.mii_region.Assign(info.mii_region);
- bf.mouth_type.Assign(info.mouth_type);
- bf.glasses_scale.Assign(info.glasses_scale);
- bf.eye_y.Assign(info.eye_y);
-
- bf.mustache_type.Assign(info.mustache_type);
- bf.eyebrow_type.Assign(info.eyebrow_type);
- bf.beard_type.Assign(info.beard_type);
- bf.nose_type.Assign(info.nose_type);
- bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
- bf.nose_y.Assign(info.nose_y);
- bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
- bf.mouth_y.Assign(info.mouth_y);
-
- bf.eye_rotate.Assign(info.eye_rotate);
- bf.mustache_y.Assign(info.mustache_y);
- bf.eye_aspect.Assign(info.eye_aspect_ratio);
- bf.glasses_y.Assign(info.glasses_y);
- bf.eye_scale.Assign(info.eye_scale);
- bf.mole_x.Assign(info.mole_x);
- bf.mole_y.Assign(info.mole_y);
-
- bf.glasses_type.Assign(info.glasses_type);
- bf.face_type.Assign(info.face_type);
- bf.favorite_color.Assign(info.favorite_color);
- bf.face_wrinkle.Assign(info.face_wrinkle);
- bf.face_color.Assign(info.face_color);
- bf.eye_x.Assign(info.eye_x);
- bf.face_makeup.Assign(info.face_makeup);
-
- bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
- bf.eyebrow_scale.Assign(info.eyebrow_scale);
- bf.eyebrow_y.Assign(info.eyebrow_y);
- bf.eyebrow_x.Assign(info.eyebrow_x);
- bf.mouth_scale.Assign(info.mouth_scale);
- bf.nose_scale.Assign(info.nose_scale);
- bf.mole_scale.Assign(info.mole_scale);
- bf.mustache_scale.Assign(info.mustache_scale);
-
- std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
-
- return out;
-}
-
-} // namespace
-
-std::ostream& operator<<(std::ostream& os, Source source) {
- if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) {
- return os << "[UNKNOWN SOURCE]";
- }
-
- os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
- return os;
-}
-
-std::u16string MiiInfo::Name() const {
- return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
-}
-
-bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
- return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
-}
-
-bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
- return !operator==(lhs, rhs);
-}
-
-std::u16string MiiStoreData::Name() const {
- return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
-}
-
-MiiManager::MiiManager() = default;
-
-MiiManager::~MiiManager() = default;
-
-MiiInfo MiiManager::CreateRandom(RandomParameters params) {
- LOG_WARNING(Service_Mii,
- "(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
- params.unknown_1, params.unknown_2, params.unknown_3);
-
- return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
-}
-
-MiiInfo MiiManager::CreateDefault(u32 index) {
- const auto new_mii = CreateMiiWithUniqueUUID();
-
- database.miis.at(index) = new_mii;
-
- EnsureDatabasePartition();
- return ConvertStoreDataToInfo(new_mii);
-}
-
-bool MiiManager::CheckUpdatedFlag() const {
- return updated_flag;
-}
-
-void MiiManager::ResetUpdatedFlag() {
- updated_flag = false;
-}
-
-bool MiiManager::IsTestModeEnabled() const {
- return is_test_mode_enabled;
-}
-
-bool MiiManager::Empty() const {
- return Size() == 0;
-}
-
-bool MiiManager::Full() const {
- return Size() == MAX_MIIS;
-}
-
-void MiiManager::Clear() {
- updated_flag = true;
- std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
-}
-
-u32 MiiManager::Size() const {
- return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
- [](const MiiStoreData& elem) { return elem.uuid; }));
-}
-
-MiiInfo MiiManager::GetInfo(u32 index) const {
- return ConvertStoreDataToInfo(GetStoreData(index));
-}
-
-MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
- return {GetInfo(index), Source::Database};
-}
-
-MiiStoreData MiiManager::GetStoreData(u32 index) const {
- return database.miis.at(index);
-}
-
-MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
- return {GetStoreData(index), Source::Database};
-}
-
-bool MiiManager::Remove(Common::UUID uuid) {
- const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
- [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
-
- if (iter == database.miis.end())
- return false;
-
- updated_flag = true;
- *iter = MiiStoreData{};
- EnsureDatabasePartition();
- return true;
-}
-
-u32 MiiManager::IndexOf(Common::UUID uuid) const {
- const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
- [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
-
- if (iter == database.miis.end())
- return INVALID_INDEX;
-
- return static_cast<u32>(std::distance(database.miis.begin(), iter));
-}
-
-u32 MiiManager::IndexOf(const MiiInfo& info) const {
- const auto iter =
- std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
- return ConvertStoreDataToInfo(elem) == info;
- });
-
- if (iter == database.miis.end())
- return INVALID_INDEX;
-
- return static_cast<u32>(std::distance(database.miis.begin(), iter));
-}
-
-bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
- const auto index = IndexOf(uuid);
-
- if (index == INVALID_INDEX || new_index >= MAX_MIIS)
- return false;
-
- updated_flag = true;
- const auto moving = database.miis[index];
- const auto replacing = database.miis[new_index];
- if (replacing.uuid) {
- database.miis[index] = replacing;
- database.miis[new_index] = moving;
- } else {
- database.miis[index] = MiiStoreData{};
- database.miis[new_index] = moving;
- }
-
- EnsureDatabasePartition();
- return true;
-}
-
-bool MiiManager::AddOrReplace(const MiiStoreData& data) {
- const auto index = IndexOf(data.uuid);
-
- updated_flag = true;
- if (index == INVALID_INDEX) {
- const auto size = Size();
- if (size == MAX_MIIS)
- return false;
- database.miis[size] = data;
- } else {
- database.miis[index] = data;
- }
-
- return true;
-}
-
-bool MiiManager::DestroyFile() {
- database = DEFAULT_MII_DATABASE;
- updated_flag = false;
- return DeleteFile();
-}
-
-bool MiiManager::DeleteFile() {
- const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
- return FileUtil::Exists(path) && FileUtil::Delete(path);
-}
-
-void MiiManager::WriteToFile() {
- const auto raw_path =
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
- if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
- FileUtil::Delete(raw_path);
-
- const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
-
- if (!FileUtil::CreateFullPath(path)) {
- LOG_WARNING(Service_Mii,
- "Failed to create full path of MiiDatabase.dat. Create the directory "
- "nand/system/save/8000000000000030 to mitigate this "
- "issue.");
- return;
- }
-
- FileUtil::IOFile save(path, "wb");
-
- if (!save.IsOpen()) {
- LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
- "made in current session will be saved.");
- return;
- }
-
- save.Resize(sizeof(MiiDatabase));
- if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
- LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
- "and/or regenerated on next run.");
- save.Resize(0);
- }
-}
-
-void MiiManager::ReadFromFile() {
- FileUtil::IOFile save(
- FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
-
- if (!save.IsOpen()) {
- LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
- "blank Mii database with no Miis.");
- std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
- return;
- }
-
- if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
- LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
- "Mii database with no Miis.");
- std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
- return;
- }
-
- EnsureDatabasePartition();
-}
-
-MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
- auto new_mii = DEFAULT_MII;
-
- do {
- new_mii.uuid = Common::UUID::Generate();
- } while (IndexOf(new_mii.uuid) != INVALID_INDEX);
-
- return new_mii;
-}
-
-void MiiManager::EnsureDatabasePartition() {
- std::stable_partition(database.miis.begin(), database.miis.end(),
- [](const MiiStoreData& elem) { return elem.uuid; });
-}
-
-} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
deleted file mode 100644
index fc742816a..000000000
--- a/src/core/hle/service/mii/mii_manager.h
+++ /dev/null
@@ -1,273 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
-#include "common/uuid.h"
-
-namespace Service::Mii {
-
-constexpr std::size_t MAX_MIIS{100};
-constexpr u32 INVALID_INDEX{0xFFFFFFFF};
-
-struct RandomParameters {
- u32 unknown_1{};
- u32 unknown_2{};
- u32 unknown_3{};
-};
-static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
-
-enum class Source : u32 {
- Database = 0,
- Default = 1,
- Account = 2,
- Friend = 3,
-};
-
-std::ostream& operator<<(std::ostream& os, Source source);
-
-struct MiiInfo {
- Common::UUID uuid{Common::INVALID_UUID};
- std::array<char16_t, 11> name{};
- u8 font_region{};
- u8 favorite_color{};
- u8 gender{};
- u8 height{};
- u8 weight{};
- u8 mii_type{};
- u8 mii_region{};
- u8 face_type{};
- u8 face_color{};
- u8 face_wrinkle{};
- u8 face_makeup{};
- u8 hair_type{};
- u8 hair_color{};
- bool hair_flip{};
- u8 eye_type{};
- u8 eye_color{};
- u8 eye_scale{};
- u8 eye_aspect_ratio{};
- u8 eye_rotate{};
- u8 eye_x{};
- u8 eye_y{};
- u8 eyebrow_type{};
- u8 eyebrow_color{};
- u8 eyebrow_scale{};
- u8 eyebrow_aspect_ratio{};
- u8 eyebrow_rotate{};
- u8 eyebrow_x{};
- u8 eyebrow_y{};
- u8 nose_type{};
- u8 nose_scale{};
- u8 nose_y{};
- u8 mouth_type{};
- u8 mouth_color{};
- u8 mouth_scale{};
- u8 mouth_aspect_ratio{};
- u8 mouth_y{};
- u8 facial_hair_color{};
- u8 beard_type{};
- u8 mustache_type{};
- u8 mustache_scale{};
- u8 mustache_y{};
- u8 glasses_type{};
- u8 glasses_color{};
- u8 glasses_scale{};
- u8 glasses_y{};
- u8 mole_type{};
- u8 mole_scale{};
- u8 mole_x{};
- u8 mole_y{};
- INSERT_PADDING_BYTES(1);
-
- std::u16string Name() const;
-};
-static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
-static_assert(std::has_unique_object_representations_v<MiiInfo>,
- "All bits of MiiInfo must contribute to its value.");
-
-bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
-bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
-
-#pragma pack(push, 4)
-struct MiiInfoElement {
- MiiInfo info{};
- Source source{};
-};
-static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
-
-struct MiiStoreBitFields {
- union {
- u32 word_0{};
-
- BitField<24, 8, u32> hair_type;
- BitField<23, 1, u32> mole_type;
- BitField<16, 7, u32> height;
- BitField<15, 1, u32> hair_flip;
- BitField<8, 7, u32> weight;
- BitField<0, 7, u32> hair_color;
- };
-
- union {
- u32 word_1{};
-
- BitField<31, 1, u32> gender;
- BitField<24, 7, u32> eye_color;
- BitField<16, 7, u32> eyebrow_color;
- BitField<8, 7, u32> mouth_color;
- BitField<0, 7, u32> facial_hair_color;
- };
-
- union {
- u32 word_2{};
-
- BitField<31, 1, u32> mii_type;
- BitField<24, 7, u32> glasses_color;
- BitField<22, 2, u32> font_region;
- BitField<16, 6, u32> eye_type;
- BitField<14, 2, u32> mii_region;
- BitField<8, 6, u32> mouth_type;
- BitField<5, 3, u32> glasses_scale;
- BitField<0, 5, u32> eye_y;
- };
-
- union {
- u32 word_3{};
-
- BitField<29, 3, u32> mustache_type;
- BitField<24, 5, u32> eyebrow_type;
- BitField<21, 3, u32> beard_type;
- BitField<16, 5, u32> nose_type;
- BitField<13, 3, u32> mouth_aspect;
- BitField<8, 5, u32> nose_y;
- BitField<5, 3, u32> eyebrow_aspect;
- BitField<0, 5, u32> mouth_y;
- };
-
- union {
- u32 word_4{};
-
- BitField<29, 3, u32> eye_rotate;
- BitField<24, 5, u32> mustache_y;
- BitField<21, 3, u32> eye_aspect;
- BitField<16, 5, u32> glasses_y;
- BitField<13, 3, u32> eye_scale;
- BitField<8, 5, u32> mole_x;
- BitField<0, 5, u32> mole_y;
- };
-
- union {
- u32 word_5{};
-
- BitField<24, 5, u32> glasses_type;
- BitField<20, 4, u32> face_type;
- BitField<16, 4, u32> favorite_color;
- BitField<12, 4, u32> face_wrinkle;
- BitField<8, 4, u32> face_color;
- BitField<4, 4, u32> eye_x;
- BitField<0, 4, u32> face_makeup;
- };
-
- union {
- u32 word_6{};
-
- BitField<28, 4, u32> eyebrow_rotate;
- BitField<24, 4, u32> eyebrow_scale;
- BitField<20, 4, u32> eyebrow_y;
- BitField<16, 4, u32> eyebrow_x;
- BitField<12, 4, u32> mouth_scale;
- BitField<8, 4, u32> nose_scale;
- BitField<4, 4, u32> mole_scale;
- BitField<0, 4, u32> mustache_scale;
- };
-};
-static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
-static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
- "MiiStoreBitFields is not trivially copyable.");
-
-struct MiiStoreData {
- // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
- // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
- // not suitable for our uses.
- std::array<u8, 0x1C> data{};
- static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
-
- std::array<char16_t, 10> name{};
- Common::UUID uuid{Common::INVALID_UUID};
- u16 crc_1{};
- u16 crc_2{};
-
- std::u16string Name() const;
-};
-static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
-
-struct MiiStoreDataElement {
- MiiStoreData data{};
- Source source{};
-};
-static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
-
-struct MiiDatabase {
- u32 magic{}; // 'NFDB'
- std::array<MiiStoreData, MAX_MIIS> miis{};
- INSERT_PADDING_BYTES(1);
- u8 count{};
- u16 crc{};
-};
-static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
-#pragma pack(pop)
-
-// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
-// with providing an easy interface for HLE emulation of the mii service.
-class MiiManager {
-public:
- MiiManager();
- ~MiiManager();
-
- MiiInfo CreateRandom(RandomParameters params);
- MiiInfo CreateDefault(u32 index);
-
- bool CheckUpdatedFlag() const;
- void ResetUpdatedFlag();
-
- bool IsTestModeEnabled() const;
-
- bool Empty() const;
- bool Full() const;
-
- void Clear();
-
- u32 Size() const;
-
- MiiInfo GetInfo(u32 index) const;
- MiiInfoElement GetInfoElement(u32 index) const;
- MiiStoreData GetStoreData(u32 index) const;
- MiiStoreDataElement GetStoreDataElement(u32 index) const;
-
- bool Remove(Common::UUID uuid);
- u32 IndexOf(Common::UUID uuid) const;
- u32 IndexOf(const MiiInfo& info) const;
-
- bool Move(Common::UUID uuid, u32 new_index);
- bool AddOrReplace(const MiiStoreData& data);
-
- bool DestroyFile();
- bool DeleteFile();
-
-private:
- void WriteToFile();
- void ReadFromFile();
-
- MiiStoreData CreateMiiWithUniqueUUID() const;
-
- void EnsureDatabasePartition();
-
- MiiDatabase database;
- bool updated_flag{};
- bool is_test_mode_enabled{};
-};
-
-}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/raw_data.cpp
new file mode 100644
index 000000000..25d7bae0c
--- /dev/null
+++ b/src/core/hle/service/mii/raw_data.cpp
@@ -0,0 +1,2261 @@
+// MIT License
+//
+// Copyright (c) Ryujinx Team and Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/hle/service/mii/raw_data.h"
+
+namespace Service::Mii::RawData {
+
+const std::array<u8, 1728> DefaultMii{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00,
+ 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00,
+ 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00,
+ 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiFaceline{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 1200> RandomMiiFacelineColor{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiFacelineWrinkle{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiFacelineMakeup{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiHairType{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x56, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x42, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
+ 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x2d, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x49, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00,
+ 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
+ 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
+ 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00,
+ 0x51, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+ 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x46, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
+ 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2d, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2d, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x25, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00,
+ 0x2f, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
+ 0x43, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+ 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x4a, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
+ 0x4d, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
+ 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
+ 0x47, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
+ 0x3a, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x4a, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
+ 0x4f, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
+ 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x3a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x51, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+ 0x45, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 1800> RandomMiiHairColor{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiEyeType{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2b, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x35, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
+ 0x2f, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
+ 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x35, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x2f, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x29, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x29, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x35, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x25, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x35, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
+ 0x39, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+ 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+ 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+ 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 588> RandomMiiEyeColor{
+ 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiEyebrowType{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiNoseType{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 3672> RandomMiiMouthType{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x1d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+const std::array<u8, 588> RandomMiiGlassType{
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
+ 0x56, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x4e, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
new file mode 100644
index 000000000..a02a5c0fd
--- /dev/null
+++ b/src/core/hle/service/mii/raw_data.h
@@ -0,0 +1,27 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Service::Mii::RawData {
+
+extern const std::array<u8, 1728> DefaultMii;
+extern const std::array<u8, 3672> RandomMiiFaceline;
+extern const std::array<u8, 1200> RandomMiiFacelineColor;
+extern const std::array<u8, 3672> RandomMiiFacelineWrinkle;
+extern const std::array<u8, 3672> RandomMiiFacelineMakeup;
+extern const std::array<u8, 3672> RandomMiiHairType;
+extern const std::array<u8, 1800> RandomMiiHairColor;
+extern const std::array<u8, 3672> RandomMiiEyeType;
+extern const std::array<u8, 588> RandomMiiEyeColor;
+extern const std::array<u8, 3672> RandomMiiEyebrowType;
+extern const std::array<u8, 3672> RandomMiiNoseType;
+extern const std::array<u8, 3672> RandomMiiMouthType;
+extern const std::array<u8, 588> RandomMiiGlassType;
+
+} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h
new file mode 100644
index 000000000..d65a1055e
--- /dev/null
+++ b/src/core/hle/service/mii/types.h
@@ -0,0 +1,67 @@
+// Copyright 2020 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"
+
+namespace Service::Mii {
+
+enum class Age : u32 {
+ Young,
+ Normal,
+ Old,
+ All,
+};
+
+enum class BeardType : u32 {
+ None,
+ Beard1,
+ Beard2,
+ Beard3,
+ Beard4,
+ Beard5,
+};
+
+enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache };
+DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
+
+enum class FontRegion : u32 {
+ Standard,
+ China,
+ Korea,
+ Taiwan,
+};
+
+enum class Gender : u32 {
+ Male,
+ Female,
+ All,
+ Maximum = Female,
+};
+
+enum class HairFlip : u32 {
+ Left,
+ Right,
+ Maximum = Right,
+};
+
+enum class MustacheType : u32 {
+ None,
+ Mustache1,
+ Mustache2,
+ Mustache3,
+ Mustache4,
+ Mustache5,
+};
+
+enum class Race : u32 {
+ Black,
+ White,
+ Asian,
+ All,
+};
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index def63dc8a..25c24e537 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, "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"},
+ {0, &MM_U::InitializeOld, "InitializeOld"},
+ {1, &MM_U::FinalizeOld, "FinalizeOld"},
+ {2, &MM_U::SetAndWaitOld, "SetAndWaitOld"},
+ {3, &MM_U::GetOld, "GetOld"},
+ {4, &MM_U::Initialize, "Initialize"},
+ {5, &MM_U::Finalize, "Finalize"},
+ {6, &MM_U::SetAndWait, "SetAndWait"},
+ {7, &MM_U::Get, "Get"},
};
// clang-format on
@@ -29,21 +29,21 @@ public:
}
private:
- void Initialize(Kernel::HLERequestContext& ctx) {
+ void InitializeOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
- void Finalize(Kernel::HLERequestContext& ctx) {
+ void FinalizeOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
- void SetAndWait(Kernel::HLERequestContext& ctx) {
+ void SetAndWaitOld(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
min = rp.Pop<u32>();
max = rp.Pop<u32>();
@@ -54,7 +54,7 @@ private:
rb.Push(RESULT_SUCCESS);
}
- void Get(Kernel::HLERequestContext& ctx) {
+ void GetOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
@@ -62,7 +62,7 @@ private:
rb.Push(current);
}
- void InitializeWithId(Kernel::HLERequestContext& ctx) {
+ void Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
@@ -70,14 +70,14 @@ private:
rb.Push<u32>(id); // Any non zero value
}
- void FinalizeWithId(Kernel::HLERequestContext& ctx) {
+ void Finalize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
- void SetAndWaitWithId(Kernel::HLERequestContext& ctx) {
+ void SetAndWait(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 input_id = rp.Pop<u32>();
min = rp.Pop<u32>();
@@ -90,7 +90,7 @@ private:
rb.Push(RESULT_SUCCESS);
}
- void GetWithId(Kernel::HLERequestContext& ctx) {
+ void Get(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp
index 89e283ca5..e38dea1f4 100644
--- a/src/core/hle/service/ncm/ncm.cpp
+++ b/src/core/hle/service/ncm/ncm.cpp
@@ -28,16 +28,16 @@ public:
{7, nullptr, "ResolveApplicationLegalInformationPath"},
{8, nullptr, "RedirectApplicationLegalInformationPath"},
{9, nullptr, "Refresh"},
- {10, nullptr, "RedirectProgramPath2"},
- {11, nullptr, "Refresh2"},
- {12, nullptr, "DeleteProgramPath"},
- {13, nullptr, "DeleteApplicationControlPath"},
- {14, nullptr, "DeleteApplicationHtmlDocumentPath"},
- {15, nullptr, "DeleteApplicationLegalInformationPath"},
- {16, nullptr, ""},
- {17, nullptr, ""},
- {18, nullptr, ""},
- {19, nullptr, ""},
+ {10, nullptr, "RedirectApplicationProgramPath"},
+ {11, nullptr, "ClearApplicationRedirection"},
+ {12, nullptr, "EraseProgramRedirection"},
+ {13, nullptr, "EraseApplicationControlRedirection"},
+ {14, nullptr, "EraseApplicationHtmlDocumentRedirection"},
+ {15, nullptr, "EraseApplicationLegalInformationRedirection"},
+ {16, nullptr, "ResolveProgramPathForDebug"},
+ {17, nullptr, "RedirectProgramPathForDebug"},
+ {18, nullptr, "RedirectApplicationProgramPathForDebug"},
+ {19, nullptr, "EraseProgramRedirectionForDebug"},
};
// clang-format on
@@ -122,6 +122,7 @@ public:
{11, nullptr, "ActivateContentMetaDatabase"},
{12, nullptr, "InactivateContentMetaDatabase"},
{13, nullptr, "InvalidateRightsIdCache"},
+ {14, nullptr, "GetMemoryReport"},
};
// clang-format on
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index b7b34ce7e..780ea30fe 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -198,9 +198,9 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{1, nullptr, "Finalize"},
- {2, nullptr, "GetState"},
- {3, nullptr, "IsNfcEnabled"},
- {100, nullptr, "SetNfcEnabled"},
+ {2, nullptr, "GetStateOld"},
+ {3, nullptr, "IsNfcEnabledOld"},
+ {100, nullptr, "SetNfcEnabledOld"},
{400, nullptr, "InitializeSystem"},
{401, nullptr, "FinalizeSystem"},
{402, nullptr, "GetState"},
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 4b79eb81d..a0469ffbd 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
#include <atomic>
#include "common/logging/log.h"
@@ -72,10 +73,10 @@ private:
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);
+ std::array<u8, 0x15> padding_1;
u32_le protocol;
u32_le tag_type;
- INSERT_PADDING_BYTES(0x2c);
+ std::array<u8, 0x2c> padding_2;
};
static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
@@ -127,7 +128,7 @@ private:
const u32 array_size = rp.Pop<u32>();
LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
- ctx.WriteBuffer(&device_handle, sizeof(device_handle));
+ ctx.WriteBuffer(device_handle);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -213,14 +214,16 @@ private:
LOG_DEBUG(Service_NFP, "called");
IPC::ResponseBuilder rb{ctx, 2};
- auto amiibo = nfp_interface.GetAmiiboBuffer();
- TagInfo tag_info{};
- tag_info.uuid = amiibo.uuid;
- 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));
+ const auto& amiibo = nfp_interface.GetAmiiboBuffer();
+ const TagInfo tag_info{
+ .uuid = amiibo.uuid,
+ .uuid_length = static_cast<u8>(tag_info.uuid.size()),
+ .padding_1 = {},
+ .protocol = 1, // TODO(ogniK): Figure out actual values
+ .tag_type = 2,
+ .padding_2 = {},
+ };
+ ctx.WriteBuffer(tag_info);
rb.Push(RESULT_SUCCESS);
}
@@ -236,8 +239,8 @@ private:
LOG_DEBUG(Service_NFP, "called");
IPC::ResponseBuilder rb{ctx, 2};
- auto amiibo = nfp_interface.GetAmiiboBuffer();
- ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
+ const auto& amiibo = nfp_interface.GetAmiiboBuffer();
+ ctx.WriteBuffer(amiibo.model_info);
rb.Push(RESULT_SUCCESS);
}
@@ -283,7 +286,7 @@ private:
CommonInfo common_info{};
common_info.application_area_size = 0;
- ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
+ ctx.WriteBuffer(common_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 767158444..db7ec6d0e 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -9,6 +9,7 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/service.h"
+#include "core/network/network.h"
#include "core/settings.h"
namespace Service::NIFM {
@@ -61,7 +62,7 @@ public:
{18, nullptr, "SetRequirementByRevision"},
{19, nullptr, "GetRequirement"},
{20, nullptr, "GetRevision"},
- {21, nullptr, "GetAppletInfo"},
+ {21, &IRequest::GetAppletInfo, "GetAppletInfo"},
{22, nullptr, "GetAdditionalInfo"},
{23, nullptr, "SetKeptInSleep"},
{24, nullptr, "RegisterSocketDescriptor"},
@@ -124,6 +125,16 @@ private:
rb.Push(RESULT_SUCCESS);
}
+ void GetAppletInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 8};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+ rb.Push<u32>(0);
+ rb.Push<u32>(0);
+ }
+
Kernel::EventPair event1, event2;
};
@@ -174,10 +185,21 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+ void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ const auto [ipv4, error] = Network::GetHostIPv4Address();
+ UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(ipv4);
+ }
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
- ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size");
+ ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c,
+ "SfNetworkProfileData is not the correct size");
u128 uuid{};
auto buffer = ctx.ReadBuffer();
std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
@@ -234,7 +256,7 @@ IGeneralService::IGeneralService(Core::System& system)
{9, nullptr, "SetNetworkProfile"},
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
{11, nullptr, "GetScanDataOld"},
- {12, nullptr, "GetCurrentIpAddress"},
+ {12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"},
{13, nullptr, "GetCurrentAccessPointOld"},
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
{15, nullptr, "GetCurrentIpConfigInfo"},
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index e85f123e2..11aa74828 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -15,6 +15,66 @@
namespace Service::NIM {
+class IShopServiceAsync final : public ServiceFramework<IShopServiceAsync> {
+public:
+ IShopServiceAsync() : ServiceFramework("IShopServiceAsync") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Cancel"},
+ {1, nullptr, "GetSize"},
+ {2, nullptr, "Read"},
+ {3, nullptr, "GetErrorCode"},
+ {4, nullptr, "Request"},
+ {5, nullptr, "Prepare"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IShopServiceAccessor final : public ServiceFramework<IShopServiceAccessor> {
+public:
+ IShopServiceAccessor() : ServiceFramework("IShopServiceAccessor") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IShopServiceAccessor::CreateAsyncInterface, "CreateAsyncInterface"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void CreateAsyncInterface(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IShopServiceAsync>();
+ }
+};
+
+class IShopServiceAccessServer final : public ServiceFramework<IShopServiceAccessServer> {
+public:
+ IShopServiceAccessServer() : ServiceFramework("IShopServiceAccessServer") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IShopServiceAccessServer::CreateAccessorInterface, "CreateAccessorInterface"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void CreateAccessorInterface(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IShopServiceAccessor>();
+ }
+};
+
class NIM final : public ServiceFramework<NIM> {
public:
explicit NIM() : ServiceFramework{"nim"} {
@@ -61,11 +121,83 @@ public:
{39, nullptr, "PrepareShutdown"},
{40, nullptr, "ListApplyDeltaTask"},
{41, nullptr, "ClearNotEnoughSpaceStateOfApplyDeltaTask"},
- {42, nullptr, "Unknown1"},
- {43, nullptr, "Unknown2"},
- {44, nullptr, "Unknown3"},
- {45, nullptr, "Unknown4"},
- {46, nullptr, "Unknown5"},
+ {42, nullptr, "Unknown42"},
+ {43, nullptr, "Unknown43"},
+ {44, nullptr, "Unknown44"},
+ {45, nullptr, "Unknown45"},
+ {46, nullptr, "Unknown46"},
+ {47, nullptr, "Unknown47"},
+ {48, nullptr, "Unknown48"},
+ {49, nullptr, "Unknown49"},
+ {50, nullptr, "Unknown50"},
+ {51, nullptr, "Unknown51"},
+ {52, nullptr, "Unknown52"},
+ {53, nullptr, "Unknown53"},
+ {54, nullptr, "Unknown54"},
+ {55, nullptr, "Unknown55"},
+ {56, nullptr, "Unknown56"},
+ {57, nullptr, "Unknown57"},
+ {58, nullptr, "Unknown58"},
+ {59, nullptr, "Unknown59"},
+ {60, nullptr, "Unknown60"},
+ {61, nullptr, "Unknown61"},
+ {62, nullptr, "Unknown62"},
+ {63, nullptr, "Unknown63"},
+ {64, nullptr, "Unknown64"},
+ {65, nullptr, "Unknown65"},
+ {66, nullptr, "Unknown66"},
+ {67, nullptr, "Unknown67"},
+ {68, nullptr, "Unknown68"},
+ {69, nullptr, "Unknown69"},
+ {70, nullptr, "Unknown70"},
+ {71, nullptr, "Unknown71"},
+ {72, nullptr, "Unknown72"},
+ {73, nullptr, "Unknown73"},
+ {74, nullptr, "Unknown74"},
+ {75, nullptr, "Unknown75"},
+ {76, nullptr, "Unknown76"},
+ {77, nullptr, "Unknown77"},
+ {78, nullptr, "Unknown78"},
+ {79, nullptr, "Unknown79"},
+ {80, nullptr, "Unknown80"},
+ {81, nullptr, "Unknown81"},
+ {82, nullptr, "Unknown82"},
+ {83, nullptr, "Unknown83"},
+ {84, nullptr, "Unknown84"},
+ {85, nullptr, "Unknown85"},
+ {86, nullptr, "Unknown86"},
+ {87, nullptr, "Unknown87"},
+ {88, nullptr, "Unknown88"},
+ {89, nullptr, "Unknown89"},
+ {90, nullptr, "Unknown90"},
+ {91, nullptr, "Unknown91"},
+ {92, nullptr, "Unknown92"},
+ {93, nullptr, "Unknown93"},
+ {94, nullptr, "Unknown94"},
+ {95, nullptr, "Unknown95"},
+ {96, nullptr, "Unknown96"},
+ {97, nullptr, "Unknown97"},
+ {98, nullptr, "Unknown98"},
+ {99, nullptr, "Unknown99"},
+ {100, nullptr, "Unknown100"},
+ {101, nullptr, "Unknown101"},
+ {102, nullptr, "Unknown102"},
+ {103, nullptr, "Unknown103"},
+ {104, nullptr, "Unknown104"},
+ {105, nullptr, "Unknown105"},
+ {106, nullptr, "Unknown106"},
+ {107, nullptr, "Unknown107"},
+ {108, nullptr, "Unknown108"},
+ {109, nullptr, "Unknown109"},
+ {110, nullptr, "Unknown110"},
+ {111, nullptr, "Unknown111"},
+ {112, nullptr, "Unknown112"},
+ {113, nullptr, "Unknown113"},
+ {114, nullptr, "Unknown114"},
+ {115, nullptr, "Unknown115"},
+ {116, nullptr, "Unknown116"},
+ {117, nullptr, "Unknown117"},
+ {118, nullptr, "Unknown118"},
};
// clang-format on
@@ -78,15 +210,24 @@ public:
explicit NIM_ECA() : ServiceFramework{"nim:eca"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "CreateServerInterface"},
+ {0, &NIM_ECA::CreateServerInterface, "CreateServerInterface"},
{1, nullptr, "RefreshDebugAvailability"},
{2, nullptr, "ClearDebugResponse"},
{3, nullptr, "RegisterDebugResponse"},
+ {4, nullptr, "IsLargeResourceAvailable"},
};
// clang-format on
RegisterHandlers(functions);
}
+
+private:
+ void CreateServerInterface(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IShopServiceAccessServer>();
+ }
};
class NIM_SHP final : public ServiceFramework<NIM_SHP> {
@@ -96,6 +237,8 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "RequestDeviceAuthenticationToken"},
{1, nullptr, "RequestCachedDeviceAuthenticationToken"},
+ {2, nullptr, "RequestEdgeToken"},
+ {3, nullptr, "RequestCachedEdgeToken"},
{100, nullptr, "RequestRegisterDeviceAccount"},
{101, nullptr, "RequestUnregisterDeviceAccount"},
{102, nullptr, "RequestDeviceAccountStatus"},
@@ -113,7 +256,8 @@ public:
{305, nullptr, "RequestCreateVirtualAccount"},
{306, nullptr, "RequestDeviceLinkStatus"},
{400, nullptr, "GetAccountByVirtualAccount"},
- {500, nullptr, "RequestSyncTicket"},
+ {401, nullptr, "GetVirtualAccount"},
+ {500, nullptr, "RequestSyncTicketLegacy"},
{501, nullptr, "RequestDownloadTicket"},
{502, nullptr, "RequestDownloadTicketForPrepurchasedContents"},
{503, nullptr, "RequestSyncTicket"},
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
index aa171473b..8fa16fb08 100644
--- a/src/core/hle/service/npns/npns.cpp
+++ b/src/core/hle/service/npns/npns.cpp
@@ -30,6 +30,7 @@ public:
{23, nullptr, "DestroyToken"},
{24, nullptr, "DestroyTokenWithApplicationId"},
{25, nullptr, "QueryIsTokenValid"},
+ {26, nullptr, "ListenToMyApplicationId"},
{31, nullptr, "UploadTokenToBaaS"},
{32, nullptr, "DestroyTokenForBaaS"},
{33, nullptr, "CreateTokenForBaaS"},
@@ -48,6 +49,8 @@ public:
{151, nullptr, "GetStateWithHandover"},
{152, nullptr, "GetStateChangeEventWithHandover"},
{153, nullptr, "GetDropEventWithHandover"},
+ {161, nullptr, "GetRequestChangeStateCancelEvent"},
+ {162, nullptr, "RequestChangeStateForceTimedWithCancelEvent"},
{201, nullptr, "RequestChangeStateForceTimed"},
{202, nullptr, "RequestChangeStateForceAsync"},
};
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index fdab3cf78..2594e6839 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -3,8 +3,10 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/ns/errors.h"
@@ -28,8 +30,8 @@ IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountPro
IAccountProxyInterface::~IAccountProxyInterface() = default;
-IApplicationManagerInterface::IApplicationManagerInterface()
- : ServiceFramework{"IApplicationManagerInterface"} {
+IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
+ : ServiceFramework{"IApplicationManagerInterface"}, system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "ListApplicationRecord"},
@@ -104,12 +106,16 @@ IApplicationManagerInterface::IApplicationManagerInterface()
{94, nullptr, "LaunchApplication"},
{95, nullptr, "GetApplicationLaunchInfo"},
{96, nullptr, "AcquireApplicationLaunchInfo"},
- {97, nullptr, "GetMainApplicationProgramIndex2"},
+ {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{99, nullptr, "LaunchDevMenu"},
{100, nullptr, "ResetToFactorySettings"},
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
+ {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
+ {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
+ {105, nullptr, "RequestResetToFactorySettingsSecurely"},
+ {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
{200, nullptr, "CalculateUserSaveDataStatistics"},
{201, nullptr, "DeleteUserSaveDataAll"},
{210, nullptr, "DeleteUserSystemSaveData"},
@@ -191,6 +197,9 @@ IApplicationManagerInterface::IApplicationManagerInterface()
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
{1308, nullptr, "DeleteApplicationCompletelyForDebug"},
{1309, nullptr, "CleanupUnavailableAddOnContents"},
+ {1310, nullptr, "RequestMoveApplicationEntity"},
+ {1311, nullptr, "EstimateSizeToMove"},
+ {1312, nullptr, "HasMovableEntity"},
{1400, nullptr, "PrepareShutdown"},
{1500, nullptr, "FormatSdCard"},
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
@@ -241,13 +250,13 @@ IApplicationManagerInterface::IApplicationManagerInterface()
{2153, nullptr, "DeactivateRightsEnvironment"},
{2154, nullptr, "ForceActivateRightsContextForExit"},
{2155, nullptr, "UpdateRightsEnvironmentStatus"},
- {2156, nullptr, "CreateRightsEnvironmentForPreomia"},
+ {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"},
{2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
{2161, nullptr, "SetUsersToRightsEnvironment"},
{2170, nullptr, "GetRightsEnvironmentStatus"},
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
- {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
+ {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
@@ -258,6 +267,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
{2350, nullptr, "PerformAutoUpdateByApplicationId"},
{2351, nullptr, "RequestNoDownloadRightsErrorResolution"},
{2352, nullptr, "RequestResolveNoDownloadRightsError"},
+ {2353, nullptr, "GetApplicationDownloadTaskInfo"},
{2400, nullptr, "GetPromotionInfo"},
{2401, nullptr, "CountPromotionInfo"},
{2402, nullptr, "ListPromotionInfo"},
@@ -266,9 +276,12 @@ IApplicationManagerInterface::IApplicationManagerInterface()
{2500, nullptr, "ConfirmAvailableTime"},
{2510, nullptr, "CreateApplicationResource"},
{2511, nullptr, "GetApplicationResource"},
- {2513, nullptr, "LaunchPreomia"},
+ {2513, nullptr, "LaunchMicroApplication"},
{2514, nullptr, "ClearTaskOfAsyncTaskManager"},
+ {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
+ {2516, nullptr, "EnsureApplicationCertificate"},
{2800, nullptr, "GetApplicationIdOfPreomia"},
+ {9999, nullptr, "GetApplicationCertificate"},
};
// clang-format on
@@ -286,7 +299,8 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC
const auto size = ctx.GetWriteBufferSize();
- const FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
std::vector<u8> out;
@@ -355,15 +369,21 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
// Get language code from settings
- const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
+ const auto language_code =
+ Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
// Convert to application language, get priority list
const auto application_language = ConvertToApplicationLanguage(language_code);
if (application_language == std::nullopt) {
+ LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
+ language_code);
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
}
const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
if (!priority_list) {
+ LOG_ERROR(Service_NS,
+ "Could not find application language priorities! application_language={}",
+ *application_language);
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
}
@@ -375,6 +395,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
}
}
+ LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}",
+ supported_languages);
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
}
@@ -399,6 +421,7 @@ ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguag
const auto language_code =
ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
if (language_code == std::nullopt) {
+ LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language);
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
}
@@ -426,8 +449,8 @@ IApplicationVersionInterface::IApplicationVersionInterface()
IApplicationVersionInterface::~IApplicationVersionInterface() = default;
-IContentManagerInterface::IContentManagerInterface()
- : ServiceFramework{"IContentManagerInterface"} {
+IContentManagementInterface::IContentManagementInterface()
+ : ServiceFramework{"IContentManagementInterface"} {
// clang-format off
static const FunctionInfo functions[] = {
{11, nullptr, "CalculateApplicationOccupiedSize"},
@@ -444,7 +467,7 @@ IContentManagerInterface::IContentManagerInterface()
RegisterHandlers(functions);
}
-IContentManagerInterface::~IContentManagerInterface() = default;
+IContentManagementInterface::~IContentManagementInterface() = default;
IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
// clang-format off
@@ -505,6 +528,10 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
{100, nullptr, "ResetToFactorySettings"},
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
+ {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
+ {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
+ {105, nullptr, "RequestResetToFactorySettingsSecurely"},
+ {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
};
// clang-format on
@@ -513,16 +540,16 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
IFactoryResetInterface::~IFactoryResetInterface() = default;
-NS::NS(const char* name) : ServiceFramework{name} {
+NS::NS(const char* name, Core::System& system_) : ServiceFramework{name}, system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
{7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
- {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
+ {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"},
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
- {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
+ {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
};
// clang-format on
@@ -533,7 +560,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
NS::~NS() = default;
std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
- return GetInterface<IApplicationManagerInterface>();
+ return GetInterface<IApplicationManagerInterface>(system);
}
class NS_DEV final : public ServiceFramework<NS_DEV> {
@@ -549,10 +576,13 @@ public:
{6, nullptr, "TerminateApplication"},
{7, nullptr, "PrepareLaunchProgramFromHost"},
{8, nullptr, "LaunchApplication"},
- {9, nullptr, "LaunchApplicationWithStorageId"},
- {10, nullptr, "TerminateApplication2"},
- {11, nullptr, "GetRunningApplicationProcessId"},
+ {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
+ {10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
+ {11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
{12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
+ {13, nullptr, "CreateApplicationResourceForDevelop"},
+ {14, nullptr, "IsPreomiaForDevelop"},
+ {15, nullptr, "GetApplicationProgramIdFromHost"},
};
// clang-format on
@@ -610,6 +640,10 @@ public:
{9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
{10, nullptr, "NotifySystemUpdateForContentDelivery"},
{11, nullptr, "PrepareShutdown"},
+ {12, nullptr, "Unknown12"},
+ {13, nullptr, "Unknown13"},
+ {14, nullptr, "Unknown14"},
+ {15, nullptr, "Unknown15"},
{16, nullptr, "DestroySystemUpdateTask"},
{17, nullptr, "RequestSendSystemUpdate"},
{18, nullptr, "GetSendSystemUpdateProgress"},
@@ -646,11 +680,11 @@ public:
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
- std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:rt")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:web")->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:am2", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:ec", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:rid", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:rt", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:web", system)->InstallAsService(service_manager);
std::make_shared<NS_DEV>()->InstallAsService(service_manager);
std::make_shared<NS_SU>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 13a64ad88..c90ccd755 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -6,6 +6,10 @@
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Service {
namespace FileSystem {
@@ -22,7 +26,7 @@ public:
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
public:
- explicit IApplicationManagerInterface();
+ explicit IApplicationManagerInterface(Core::System& system_);
~IApplicationManagerInterface() override;
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
@@ -32,6 +36,8 @@ private:
void GetApplicationControlData(Kernel::HLERequestContext& ctx);
void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
+
+ Core::System& system;
};
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
@@ -40,10 +46,10 @@ public:
~IApplicationVersionInterface() override;
};
-class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
+class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
public:
- explicit IContentManagerInterface();
- ~IContentManagerInterface() override;
+ explicit IContentManagementInterface();
+ ~IContentManagementInterface() override;
};
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
@@ -72,13 +78,13 @@ public:
class NS final : public ServiceFramework<NS> {
public:
- explicit NS(const char* name);
+ explicit NS(const char* name, Core::System& system_);
~NS() override;
std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
private:
- template <typename T>
+ template <typename T, typename... Args>
void PushInterface(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
@@ -87,13 +93,23 @@ private:
rb.PushIpcInterface<T>();
}
- template <typename T>
- std::shared_ptr<T> GetInterface() const {
+ void PushIApplicationManagerInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IApplicationManagerInterface>(system);
+ }
+
+ template <typename T, typename... Args>
+ std::shared_ptr<T> GetInterface(Args&&... args) const {
static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
"Not a base of ServiceFrameworkBase");
- return std::make_shared<T>();
+ return std::make_shared<T>(std::forward<Args>(args)...);
}
+
+ Core::System& system;
};
/// Registers all NS services with the specified service manager.
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 8da4e52c5..5ccec2637 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -19,6 +19,7 @@
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -49,19 +50,9 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
};
-constexpr std::array<const char*, 7> SHARED_FONTS_TTF{
- "FontStandard.ttf",
- "FontChineseSimplified.ttf",
- "FontExtendedChineseSimplified.ttf",
- "FontChineseTraditional.ttf",
- "FontKorean.ttf",
- "FontNintendoExtended.ttf",
- "FontNintendoExtended2.ttf",
-};
-
// The below data is specific to shared font data dumped from Switch on f/w 2.2
// Virtual address and offsets/sizes likely will vary by dump
-constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
+[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
@@ -162,7 +153,8 @@ PL_U::PL_U(Core::System& system)
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
{100, nullptr, "RequestApplicationFunctionAuthorization"},
- {101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"},
+ {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
+ {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
{1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
{1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
};
@@ -265,16 +257,13 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
LOG_DEBUG(Service_NS, "called");
- system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
- SHARED_FONT_MEM_SIZE,
- Kernel::MemoryState::Shared);
// Create shared font memory object
auto& kernel = system.Kernel();
- impl->shared_font_mem = Kernel::SharedMemory::Create(
- kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
- "PL_U:shared_font_mem");
+ impl->shared_font_mem = SharedFrom(&kernel.GetFontSharedMem());
+
+ std::memcpy(impl->shared_font_mem->GetPointer(), impl->shared_font->data(),
+ impl->shared_font->size());
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 1b52511a5..5681599ba 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -21,27 +21,40 @@ namespace Service::Nvidia::Devices {
/// implement the ioctl interface.
class nvdevice {
public:
- explicit nvdevice(Core::System& system) : system{system} {};
+ explicit nvdevice(Core::System& system) : system{system} {}
virtual ~nvdevice() = default;
- union Ioctl {
- u32_le raw;
- BitField<0, 8, u32> cmd;
- BitField<8, 8, u32> group;
- BitField<16, 14, u32> length;
- BitField<30, 1, u32> is_in;
- BitField<31, 1, u32> is_out;
- };
/**
- * Handles an ioctl request.
+ * Handles an ioctl1 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
- virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) = 0;
+ virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) = 0;
+
+ /**
+ * Handles an ioctl2 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
+ * @param output A buffer where the output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
+
+ /**
+ * Handles an ioctl3 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param output A buffer where the output data will be written to.
+ * @param inline_output A buffer where the inlined output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) = 0;
protected:
Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 3f7b8e670..ce615c758 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -18,11 +18,22 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvdisp_disp0 ::~nvdisp_disp0() = default;
-u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 6fcdeee84..55a33b7e4 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,9 +20,11 @@ public:
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvdisp_disp0() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
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 195421cc0..6b062e10e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -16,107 +16,152 @@
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
-namespace NvErrCodes {
-enum {
- InvalidNmapHandle = -22,
-};
-}
nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu::~nvhost_as_gpu() = default;
-u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocInitalizeExCommand:
- return InitalizeEx(input, output);
- case IoctlCommand::IocAllocateSpaceCommand:
- return AllocateSpace(input, output);
- case IoctlCommand::IocMapBufferExCommand:
- return MapBufferEx(input, output);
- case IoctlCommand::IocBindChannelCommand:
- return BindChannel(input, output);
- case IoctlCommand::IocGetVaRegionsCommand:
- return GetVARegions(input, output);
- case IoctlCommand::IocUnmapBufferCommand:
- return UnmapBuffer(input, output);
+NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 'A':
+ switch (command.cmd) {
+ case 0x1:
+ return BindChannel(input, output);
+ case 0x2:
+ return AllocateSpace(input, output);
+ case 0x3:
+ return FreeSpace(input, output);
+ case 0x5:
+ return UnmapBuffer(input, output);
+ case 0x6:
+ return MapBufferEx(input, output);
+ case 0x8:
+ return GetVARegions(input, output);
+ case 0x9:
+ return InitalizeEx(input, output);
+ case 0x14:
+ return Remap(input, output);
+ default:
+ break;
+ }
+ break;
default:
break;
}
- if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
- return Remap(input, output);
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- UNIMPLEMENTED_MSG("Unimplemented ioctl command");
- return 0;
+NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ switch (command.group) {
+ case 'A':
+ switch (command.cmd) {
+ case 0x8:
+ return GetVARegions(input, output, inline_output);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlInitalizeEx params{};
std::memcpy(&params, input.data(), input.size());
+
LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
+
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
params.page_size, params.flags);
- auto& gpu = system.GPU();
- const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
- if (params.flags & 1) {
- params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
+ const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
+ if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) {
+ params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size);
} else {
- params.offset = gpu.MemoryManager().AllocateSpace(size, params.align);
+ params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
+ }
+
+ auto result = NvResult::Success;
+ if (!params.offset) {
+ LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
+ result = NvResult::InsufficientMemory;
}
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return result;
}
-u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
- std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry);
+NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlFreeSpace params{};
+ std::memcpy(&params, input.data(), input.size());
+
+ LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
+ params.pages, params.page_size);
+
+ system.GPU().MemoryManager().Unmap(params.offset,
+ static_cast<std::size_t>(params.pages) * params.page_size);
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
+}
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries);
+NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
+ const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
+ LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
+
+ auto result = NvResult::Success;
std::vector<IoctlRemapEntry> entries(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
- auto& gpu = system.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);
- GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10;
- auto object = nvmap_dev->GetObject(entry.nvmap_handle);
+ LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
+ entry.offset, entry.nvmap_handle, entry.pages);
+
+ const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
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);
+ LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
+ result = NvResult::InvalidState;
+ break;
}
- ASSERT(object->status == nvmap::Object::Status::Allocated);
+ const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10};
+ const auto size{static_cast<u64>(entry.pages) << 0x10};
+ const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10};
+ const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)};
- const u64 size = static_cast<u64>(entry.pages) << 0x10;
- ASSERT(size <= object->size);
- const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10;
-
- const GPUVAddr returned =
- gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size);
- ASSERT(returned == offset);
+ if (!addr) {
+ LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
+ result = NvResult::InvalidState;
+ break;
+ }
}
+
std::memcpy(output.data(), entries.data(), output.size());
- return 0;
+ return result;
}
-u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), input.size());
@@ -126,79 +171,107 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
params.offset);
- if (!params.nvmap_handle) {
- return 0;
+ const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
+ if (!object) {
+ LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::InvalidState;
}
- auto object = nvmap_dev->GetObject(params.nvmap_handle);
- ASSERT(object);
-
- // We can only map objects that have already been assigned a CPU address.
- ASSERT(object->status == nvmap::Object::Status::Allocated);
-
- ASSERT(params.buffer_offset == 0);
-
// The real nvservices doesn't make a distinction between handles and ids, and
// object can only have one handle and it will be the same as its id. Assert that this is the
// case to prevent unexpected behavior.
ASSERT(object->id == params.nvmap_handle);
-
auto& gpu = system.GPU();
- if (params.flags & 1) {
- params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size);
- } else {
- params.offset = gpu.MemoryManager().MapBufferEx(object->addr, object->size);
+ u64 page_size{params.page_size};
+ if (!page_size) {
+ page_size = object->align;
+ }
+
+ if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) {
+ if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) {
+ const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)};
+ const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)};
+
+ if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) {
+ LOG_CRITICAL(Service_NVDRV,
+ "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, "
+ "mapping_size = {}, offset={}",
+ params.flags, params.nvmap_handle, params.buffer_offset,
+ params.mapping_size, params.offset);
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::InvalidState;
+ }
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
+ } else {
+ LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::InvalidState;
+ }
}
- // Create a new mapping entry for this operation.
- ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(),
- "Offset is already mapped");
+ // We can only map objects that have already been assigned a CPU address.
+ ASSERT(object->status == nvmap::Object::Status::Allocated);
- BufferMapping mapping{};
- mapping.nvmap_handle = params.nvmap_handle;
- mapping.offset = params.offset;
- mapping.size = object->size;
+ const auto physical_address{object->addr + params.buffer_offset};
+ u64 size{params.mapping_size};
+ if (!size) {
+ size = object->size;
+ }
+
+ const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None};
+ if (is_alloc) {
+ params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size);
+ } else {
+ params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
+ }
- buffer_mappings[params.offset] = mapping;
+ auto result = NvResult::Success;
+ if (!params.offset) {
+ LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
+ result = NvResult::InvalidState;
+ } else {
+ AddBufferMap(params.offset, size, physical_address, is_alloc);
+ }
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return result;
}
-u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlUnmapBuffer params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
- const auto itr = buffer_mappings.find(params.offset);
- 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;
+ if (const auto size{RemoveBufferMap(params.offset)}; size) {
+ system.GPU().MemoryManager().Unmap(params.offset, *size);
+ } else {
+ LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset);
}
- params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
- buffer_mappings.erase(itr->second.offset);
-
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
- LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd);
channel = params.fd;
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
+
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size);
@@ -210,9 +283,67 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
params.regions[1].offset = 0x04000000;
params.regions[1].page_size = 0x10000;
params.regions[1].pages = 0x1bffff;
+
// TODO(ogniK): This probably can stay stubbed but should add support way way later
+
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
+}
+
+NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ IoctlGetVaRegions params{};
+ std::memcpy(&params, input.data(), input.size());
+
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
+ params.buf_size);
+
+ params.buf_size = 0x30;
+ params.regions[0].offset = 0x04000000;
+ params.regions[0].page_size = 0x1000;
+ params.regions[0].pages = 0x3fbfff;
+
+ params.regions[1].offset = 0x04000000;
+ params.regions[1].page_size = 0x10000;
+ params.regions[1].pages = 0x1bffff;
+
+ // TODO(ogniK): This probably can stay stubbed but should add support way way later
+
+ std::memcpy(output.data(), &params, output.size());
+ std::memcpy(inline_output.data(), &params.regions, inline_output.size());
+ return NvResult::Success;
+}
+
+std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
+ const auto end{buffer_mappings.upper_bound(gpu_addr)};
+ for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) {
+ if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) {
+ return iter->second;
+ }
+ }
+
+ return std::nullopt;
+}
+
+void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated) {
+ buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated};
+}
+
+std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
+ if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) {
+ std::size_t size{};
+
+ if (iter->second.IsAllocated()) {
+ size = iter->second.Size();
+ }
+
+ buffer_mappings.erase(iter);
+
+ return size;
+ }
+
+ return std::nullopt;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index f79fcc065..08035fa0e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -4,9 +4,12 @@
#pragma once
+#include <map>
#include <memory>
-#include <unordered_map>
+#include <optional>
#include <vector>
+
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -15,41 +18,79 @@ namespace Service::Nvidia::Devices {
class nvmap;
+enum class AddressSpaceFlags : u32 {
+ None = 0x0,
+ FixedOffset = 0x1,
+ Remap = 0x100,
+};
+DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags);
+
class nvhost_as_gpu final : public nvdevice {
public:
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_as_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocInitalizeExCommand = 0x40284109,
- IocAllocateSpaceCommand = 0xC0184102,
- IocRemapCommand = 0x00000014,
- IocMapBufferExCommand = 0xC0284106,
- IocBindChannelCommand = 0x40044101,
- IocGetVaRegionsCommand = 0xC0404108,
- IocUnmapBufferCommand = 0xC0084105,
+ class BufferMap final {
+ public:
+ constexpr BufferMap() = default;
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
+ : start_addr{start_addr}, end_addr{start_addr + size} {}
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated)
+ : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
+ is_allocated{is_allocated} {}
+
+ constexpr VAddr StartAddr() const {
+ return start_addr;
+ }
+
+ constexpr VAddr EndAddr() const {
+ return end_addr;
+ }
+
+ constexpr std::size_t Size() const {
+ return end_addr - start_addr;
+ }
+
+ constexpr VAddr CpuAddr() const {
+ return cpu_addr;
+ }
+
+ constexpr bool IsAllocated() const {
+ return is_allocated;
+ }
+
+ private:
+ GPUVAddr start_addr{};
+ GPUVAddr end_addr{};
+ VAddr cpu_addr{};
+ bool is_allocated{};
};
struct IoctlInitalizeEx {
- u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default
- s32_le as_fd; // ignored; passes 0
- u32_le flags; // passes 0
- u32_le reserved; // ignored; passes 0
- u64_le unk0;
- u64_le unk1;
- u64_le unk2;
+ u32_le big_page_size{}; // depends on GPU's available_big_page_sizes; 0=default
+ s32_le as_fd{}; // ignored; passes 0
+ u32_le flags{}; // passes 0
+ u32_le reserved{}; // ignored; passes 0
+ u64_le unk0{};
+ u64_le unk1{};
+ u64_le unk2{};
};
static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size");
struct IoctlAllocSpace {
- u32_le pages;
- u32_le page_size;
- u32_le flags;
+ u32_le pages{};
+ u32_le page_size{};
+ AddressSpaceFlags flags{};
INSERT_PADDING_WORDS(1);
union {
u64_le offset;
@@ -58,74 +99,83 @@ private:
};
static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
+ struct IoctlFreeSpace {
+ u64_le offset{};
+ u32_le pages{};
+ u32_le page_size{};
+ };
+ static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
+
struct IoctlRemapEntry {
- u16_le flags;
- u16_le kind;
- u32_le nvmap_handle;
- u32_le map_offset;
- u32_le offset;
- u32_le pages;
+ u16_le flags{};
+ u16_le kind{};
+ u32_le nvmap_handle{};
+ u32_le map_offset{};
+ u32_le offset{};
+ u32_le pages{};
};
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
struct IoctlMapBufferEx {
- u32_le flags; // bit0: fixed_offset, bit2: cacheable
- u32_le kind; // -1 is default
- u32_le nvmap_handle;
- u32_le page_size; // 0 means don't care
- u64_le buffer_offset;
- u64_le mapping_size;
- u64_le offset;
+ AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable
+ u32_le kind{}; // -1 is default
+ u32_le nvmap_handle{};
+ u32_le page_size{}; // 0 means don't care
+ s64_le buffer_offset{};
+ u64_le mapping_size{};
+ s64_le offset{};
};
static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
struct IoctlUnmapBuffer {
- u64_le offset;
+ s64_le offset{};
};
static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
struct IoctlBindChannel {
- u32_le fd;
+ s32_le fd{};
};
static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
struct IoctlVaRegion {
- u64_le offset;
- u32_le page_size;
+ u64_le offset{};
+ u32_le page_size{};
INSERT_PADDING_WORDS(1);
- u64_le pages;
+ u64_le pages{};
};
static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
struct IoctlGetVaRegions {
- u64_le buf_addr; // (contained output user ptr on linux, ignored)
- u32_le buf_size; // forced to 2*sizeof(struct va_region)
- u32_le reserved;
- IoctlVaRegion regions[2];
+ u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
+ u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
+ u32_le reserved{};
+ IoctlVaRegion regions[2]{};
};
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
"IoctlGetVaRegions is incorrect size");
- struct BufferMapping {
- u64 offset;
- u64 size;
- u32 nvmap_handle;
- };
+ s32 channel{};
- /// Map containing the nvmap object mappings in GPU memory.
- std::unordered_map<u64, BufferMapping> buffer_mappings;
+ NvResult InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
- u32 channel{};
+ NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output);
- u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
- u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
- u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
- u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
- u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
- u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
- u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
+ std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
+ void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
+ std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
std::shared_ptr<nvmap> nvmap_dev;
+
+ // This is expected to be ordered, therefore we must use a map, not unordered_map
+ std::map<GPUVAddr, BufferMap> buffer_mappings;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index b27ee0502..d90cf90a8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -15,45 +15,59 @@
namespace Service::Nvidia::Devices {
-nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
- : nvdevice(system), events_interface{events_interface} {}
+nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
+ SyncpointManager& syncpoint_manager)
+ : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
nvhost_ctrl::~nvhost_ctrl() = default;
-u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocGetConfigCommand:
- return NvOsGetConfigU32(input, output);
- case IoctlCommand::IocCtrlEventWaitCommand:
- return IocCtrlEventWait(input, output, false, ctrl);
- case IoctlCommand::IocCtrlEventWaitAsyncCommand:
- return IocCtrlEventWait(input, output, true, ctrl);
- case IoctlCommand::IocCtrlEventRegisterCommand:
- return IocCtrlEventRegister(input, output);
- case IoctlCommand::IocCtrlEventUnregisterCommand:
- return IocCtrlEventUnregister(input, output);
- case IoctlCommand::IocCtrlEventSignalCommand:
- return IocCtrlEventSignal(input, output);
+NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x1b:
+ return NvOsGetConfigU32(input, output);
+ case 0x1c:
+ return IocCtrlClearEventWait(input, output);
+ case 0x1d:
+ return IocCtrlEventWait(input, output, false);
+ case 0x1e:
+ return IocCtrlEventWait(input, output, true);
+ case 0x1f:
+ return IocCtrlEventRegister(input, output);
+ case 0x20:
+ return IocCtrlEventUnregister(input, output);
+ }
+ break;
default:
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ break;
}
+
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetConfigParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
params.param_str.data());
- return 0x30006; // Returns error on production mode
+ return NvResult::ConfigVarNotFound; // Returns error on production mode
}
-u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
- bool is_async, IoctlCtrl& ctrl) {
+NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
+ bool is_async) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -70,19 +84,33 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter;
}
+ if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
+ params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::Success;
+ }
+
+ if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
+ syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
+ params.value = new_value;
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::Success;
+ }
+
auto event = events_interface.events[event_id];
auto& gpu = system.GPU();
+
// This is mostly to take into account unimplemented features. As synced
// gpu is always synced.
if (!gpu.IsAsync()) {
- event.writable->Signal();
+ event.event.writable->Signal();
return NvResult::Success;
}
auto lock = gpu.LockSync();
- const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
+ const u32 current_syncpoint_value = event.fence.value;
const s32 diff = current_syncpoint_value - params.threshold;
if (diff >= 0) {
- event.writable->Signal();
+ event.event.writable->Signal();
params.value = current_syncpoint_value;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
@@ -109,12 +137,9 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
}
params.value |= event_id;
- event.writable->Clear();
+ event.event.writable->Clear();
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
- if (!is_async && ctrl.fresh_call) {
- ctrl.must_delay = true;
- ctrl.timeout = params.timeout;
- ctrl.event_id = event_id;
+ if (!is_async) {
return NvResult::Timeout;
}
std::memcpy(output.data(), &params, sizeof(params));
@@ -124,7 +149,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter;
}
-u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventRegisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
@@ -139,7 +164,8 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
return NvResult::Success;
}
-u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
+ std::vector<u8>& output) {
IocCtrlEventUnregisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
@@ -154,24 +180,22 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
return NvResult::Success;
}
-u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventSignalParams params{};
std::memcpy(&params, input.data(), sizeof(params));
- // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
- // It is believed from RE to cancel the GPU Event. However, better research is required
- u32 event_id = params.user_event_id & 0x00FF;
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
+
+ u32 event_id = params.event_id & 0x00FF;
+ LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
+
if (event_id >= MaxNvEvents) {
return NvResult::BadParameter;
}
if (events_interface.status[event_id] == EventState::Waiting) {
- auto& gpu = system.GPU();
- if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
- events_interface.assigned_value[event_id])) {
- events_interface.LiberateEvent(event_id);
- events_interface.events[event_id].writable->Signal();
- }
+ events_interface.LiberateEvent(event_id);
}
+
+ syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
+
return NvResult::Success;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 9898623de..c5aa1362a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -14,137 +14,120 @@ namespace Service::Nvidia::Devices {
class nvhost_ctrl final : public nvdevice {
public:
- explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
+ explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface,
+ SyncpointManager& syncpoint_manager);
~nvhost_ctrl() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocSyncptReadCommand = 0xC0080014,
- IocSyncptIncrCommand = 0x40040015,
- IocSyncptWaitCommand = 0xC00C0016,
- IocModuleMutexCommand = 0x40080017,
- IocModuleRegRDWRCommand = 0xC0180018,
- IocSyncptWaitexCommand = 0xC0100019,
- IocSyncptReadMaxCommand = 0xC008001A,
- IocGetConfigCommand = 0xC183001B,
- IocCtrlEventSignalCommand = 0xC004001C,
- IocCtrlEventWaitCommand = 0xC010001D,
- IocCtrlEventWaitAsyncCommand = 0xC010001E,
- IocCtrlEventRegisterCommand = 0xC004001F,
- IocCtrlEventUnregisterCommand = 0xC0040020,
- IocCtrlEventKillCommand = 0x40080021,
- };
struct IocSyncptReadParams {
- u32_le id;
- u32_le value;
+ u32_le id{};
+ u32_le value{};
};
static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
struct IocSyncptIncrParams {
- u32_le id;
+ u32_le id{};
};
static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
struct IocSyncptWaitParams {
- u32_le id;
- u32_le thresh;
- s32_le timeout;
+ u32_le id{};
+ u32_le thresh{};
+ s32_le timeout{};
};
static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
struct IocModuleMutexParams {
- u32_le id;
- u32_le lock; // (0 = unlock and 1 = lock)
+ u32_le id{};
+ u32_le lock{}; // (0 = unlock and 1 = lock)
};
static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
struct IocModuleRegRDWRParams {
- u32_le id;
- u32_le num_offsets;
- u32_le block_size;
- u32_le offsets;
- u32_le values;
- u32_le write;
+ u32_le id{};
+ u32_le num_offsets{};
+ u32_le block_size{};
+ u32_le offsets{};
+ u32_le values{};
+ u32_le write{};
};
static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
struct IocSyncptWaitexParams {
- u32_le id;
- u32_le thresh;
- s32_le timeout;
- u32_le value;
+ u32_le id{};
+ u32_le thresh{};
+ s32_le timeout{};
+ u32_le value{};
};
static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
struct IocSyncptReadMaxParams {
- u32_le id;
- u32_le value;
+ u32_le id{};
+ u32_le value{};
};
static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
struct IocGetConfigParams {
- std::array<char, 0x41> domain_str;
- std::array<char, 0x41> param_str;
- std::array<char, 0x101> config_str;
+ std::array<char, 0x41> domain_str{};
+ std::array<char, 0x41> param_str{};
+ std::array<char, 0x101> config_str{};
};
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
struct IocCtrlEventSignalParams {
- u32_le user_event_id;
+ u32_le event_id{};
};
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
"IocCtrlEventSignalParams is incorrect size");
struct IocCtrlEventWaitParams {
- u32_le syncpt_id;
- u32_le threshold;
- s32_le timeout;
- u32_le value;
+ u32_le syncpt_id{};
+ u32_le threshold{};
+ s32_le timeout{};
+ u32_le value{};
};
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
struct IocCtrlEventWaitAsyncParams {
- u32_le syncpt_id;
- u32_le threshold;
- u32_le timeout;
- u32_le value;
+ u32_le syncpt_id{};
+ u32_le threshold{};
+ u32_le timeout{};
+ u32_le value{};
};
static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
"IocCtrlEventWaitAsyncParams is incorrect size");
struct IocCtrlEventRegisterParams {
- u32_le user_event_id;
+ u32_le user_event_id{};
};
static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
"IocCtrlEventRegisterParams is incorrect size");
struct IocCtrlEventUnregisterParams {
- u32_le user_event_id;
+ u32_le user_event_id{};
};
static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
"IocCtrlEventUnregisterParams is incorrect size");
struct IocCtrlEventKill {
- u64_le user_events;
+ u64_le user_events{};
};
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
- u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
-
- u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
- IoctlCtrl& ctrl);
-
- u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
-
- u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
-
- u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
+ NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
EventInterface& events_interface;
+ SyncpointManager& syncpoint_manager;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index cc2192e5c..2d7ea433c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,39 +15,66 @@ namespace Service::Nvidia::Devices {
nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
-u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& input2, std::vector<u8>& output,
- std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocGetCharacteristicsCommand:
- return GetCharacteristics(input, output, output2, version);
- case IoctlCommand::IocGetTPCMasksCommand:
- return GetTPCMasks(input, output);
- case IoctlCommand::IocGetActiveSlotMaskCommand:
- return GetActiveSlotMask(input, output);
- case IoctlCommand::IocZcullGetCtxSizeCommand:
- return ZCullGetCtxSize(input, output);
- case IoctlCommand::IocZcullGetInfo:
- return ZCullGetInfo(input, output);
- case IoctlCommand::IocZbcSetTable:
- return ZBCSetTable(input, output);
- case IoctlCommand::IocZbcQueryTable:
- return ZBCQueryTable(input, output);
- case IoctlCommand::IocFlushL2:
- return FlushL2(input, output);
- case IoctlCommand::IocGetGpuTime:
- return GetGpuTime(input, output);
+NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 'G':
+ switch (command.cmd) {
+ case 0x1:
+ return ZCullGetCtxSize(input, output);
+ case 0x2:
+ return ZCullGetInfo(input, output);
+ case 0x3:
+ return ZBCSetTable(input, output);
+ case 0x4:
+ return ZBCQueryTable(input, output);
+ case 0x5:
+ return GetCharacteristics(input, output);
+ case 0x6:
+ return GetTPCMasks(input, output);
+ case 0x7:
+ return FlushL2(input, output);
+ case 0x14:
+ return GetActiveSlotMask(input, output);
+ case 0x1c:
+ return GetGpuTime(input, output);
+ default:
+ break;
+ }
+ break;
+ }
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output, std::vector<u8>& inline_output) {
+ switch (command.group) {
+ case 'G':
+ switch (command.cmd) {
+ case 0x5:
+ return GetCharacteristics(input, output, inline_output);
+ case 0x6:
+ return GetTPCMasks(input, output, inline_output);
+ default:
+ break;
+ }
+ break;
default:
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ break;
}
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
- std::vector<u8>& output2, IoctlVersion version) {
+NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
+ std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
@@ -88,31 +115,83 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
params.gc.gr_compbit_store_base_hw = 0x0;
params.gpu_characteristics_buf_size = 0xA0;
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
+}
- if (version == IoctlVersion::Version3) {
- std::memcpy(output.data(), input.data(), output.size());
- std::memcpy(output2.data(), &params.gc, output2.size());
- } else {
- std::memcpy(output.data(), &params, output.size());
+NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ LOG_DEBUG(Service_NVDRV, "called");
+ IoctlCharacteristics params{};
+ std::memcpy(&params, input.data(), input.size());
+ params.gc.arch = 0x120;
+ params.gc.impl = 0xb;
+ params.gc.rev = 0xa1;
+ params.gc.num_gpc = 0x1;
+ params.gc.l2_cache_size = 0x40000;
+ params.gc.on_board_video_memory_size = 0x0;
+ params.gc.num_tpc_per_gpc = 0x2;
+ params.gc.bus_type = 0x20;
+ params.gc.big_page_size = 0x20000;
+ params.gc.compression_page_size = 0x20000;
+ params.gc.pde_coverage_bit_count = 0x1B;
+ params.gc.available_big_page_sizes = 0x30000;
+ params.gc.gpc_mask = 0x1;
+ params.gc.sm_arch_sm_version = 0x503;
+ params.gc.sm_arch_spa_version = 0x503;
+ params.gc.sm_arch_warp_count = 0x80;
+ params.gc.gpu_va_bit_count = 0x28;
+ params.gc.reserved = 0x0;
+ params.gc.flags = 0x55;
+ params.gc.twod_class = 0x902D;
+ params.gc.threed_class = 0xB197;
+ params.gc.compute_class = 0xB1C0;
+ params.gc.gpfifo_class = 0xB06F;
+ params.gc.inline_to_memory_class = 0xA140;
+ params.gc.dma_copy_class = 0xB0B5;
+ params.gc.max_fbps_count = 0x1;
+ params.gc.fbp_en_mask = 0x0;
+ params.gc.max_ltc_per_fbp = 0x2;
+ params.gc.max_lts_per_ltc = 0x1;
+ params.gc.max_tex_per_tpc = 0x0;
+ params.gc.max_gpc_count = 0x1;
+ params.gc.rop_l2_en_mask_0 = 0x21D70;
+ params.gc.rop_l2_en_mask_1 = 0x0;
+ params.gc.chipname = 0x6230326D67;
+ params.gc.gr_compbit_store_base_hw = 0x0;
+ params.gpu_characteristics_buf_size = 0xA0;
+ params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
+
+ std::memcpy(output.data(), input.data(), output.size());
+ std::memcpy(inline_output.data(), &params.gc, inline_output.size());
+ return NvResult::Success;
+}
+
+NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlGpuGetTpcMasksArgs params{};
+ std::memcpy(&params, input.data(), input.size());
+ LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
+ if (params.mask_buffer_size != 0) {
+ params.tcp_mask = 3;
}
- return 0;
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
- LOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size,
- params.mask_buf_addr);
- // TODO(ogniK): Confirm value on hardware
- if (params.mask_buf_size)
- params.tpc_mask_size = 4 * 1; // 4 * num_gpc
- else
- params.tpc_mask_size = 0;
- std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
+ if (params.mask_buffer_size != 0) {
+ params.tcp_mask = 3;
+ }
+ std::memcpy(output.data(), &params, output.size());
+ std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size());
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlActiveSlotMask params{};
@@ -122,10 +201,10 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
params.slot = 0x07;
params.mask = 0x01;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlZcullGetCtxSize params{};
@@ -134,10 +213,10 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
}
params.size = 0x1;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -157,48 +236,47 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
params.subregion_height_align_pixels = 0x40;
params.subregion_count = 0x10;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcSetTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO(ogniK): What does this even actually do?
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcQueryTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlFlushL2 params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
- const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks());
- params.gpu_time = static_cast<u64_le>(ns.count());
+ params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 642b0a2cb..137b88238 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,32 +16,13 @@ public:
explicit nvhost_ctrl_gpu(Core::System& system);
~nvhost_ctrl_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocGetCharacteristicsCommand = 0xC0B04705,
- IocGetTPCMasksCommand = 0xC0184706,
- IocGetActiveSlotMaskCommand = 0x80084714,
- IocZcullGetCtxSizeCommand = 0x80044701,
- IocZcullGetInfo = 0x80284702,
- IocZbcSetTable = 0x402C4703,
- IocZbcQueryTable = 0xC0344704,
- IocFlushL2 = 0x40084707,
- IocInvalICache = 0x4008470D,
- IocSetMmudebugMode = 0x4008470E,
- IocSetSmDebugMode = 0x4010470F,
- IocWaitForPause = 0xC0084710,
- IocGetTcpExceptionEnStatus = 0x80084711,
- IocNumVsms = 0x80084712,
- IocVsmsMapping = 0xC0044713,
- IocGetErrorChannelUserData = 0xC008471B,
- IocGetGpuTime = 0xC010471C,
- IocGetCpuTimeCorrelationInfo = 0xC108471D,
- };
-
struct IoctlGpuCharacteristics {
u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B)
@@ -92,16 +73,11 @@ private:
"IoctlCharacteristics is incorrect size");
struct IoctlGpuGetTpcMasksArgs {
- /// [in] TPC mask buffer size reserved by userspace. Should be at least
- /// sizeof(__u32) * fls(gpc_mask) to receive TPC mask for each GPC.
- /// [out] full kernel buffer size
- u32_le mask_buf_size;
- u32_le reserved;
-
- /// [in] pointer to TPC mask buffer. It will receive one 32-bit TPC mask per GPC or 0 if
- /// GPC is not enabled or not present. This parameter is ignored if mask_buf_size is 0.
- u64_le mask_buf_addr;
- u64_le tpc_mask_size; // Nintendo add this?
+ u32_le mask_buffer_size{};
+ INSERT_PADDING_WORDS(1);
+ u64_le mask_buffer_address{};
+ u32_le tcp_mask{};
+ INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24,
"IoctlGpuGetTpcMasksArgs is incorrect size");
@@ -159,20 +135,26 @@ private:
static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
struct IoctlGetGpuTime {
- u64_le gpu_time;
+ u64_le gpu_time{};
+ INSERT_PADDING_WORDS(2);
};
- static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
-
- u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
- std::vector<u8>& output2, IoctlVersion version);
- u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
- u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
- u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
- u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
+ static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
+
+ NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output);
+
+ NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output);
+
+ NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index f1966ac0e..af8b3d9f1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -7,117 +7,148 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace Service::Nvidia::Devices {
-nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
- : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager{syncpoint_manager} {
+ channel_fence.id = syncpoint_manager.AllocateSyncpoint();
+ channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
+}
+
nvhost_gpu::~nvhost_gpu() = default;
-u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
- case IoctlCommand::IocSetClientDataCommand:
- return SetClientData(input, output);
- case IoctlCommand::IocGetClientDataCommand:
- return GetClientData(input, output);
- case IoctlCommand::IocZCullBind:
- return ZCullBind(input, output);
- case IoctlCommand::IocSetErrorNotifierCommand:
- return SetErrorNotifier(input, output);
- case IoctlCommand::IocChannelSetPriorityCommand:
- return SetChannelPriority(input, output);
- case IoctlCommand::IocAllocGPFIFOEx2Command:
- return AllocGPFIFOEx2(input, output);
- case IoctlCommand::IocAllocObjCtxCommand:
- return AllocateObjectContext(input, output);
- case IoctlCommand::IocChannelGetWaitbaseCommand:
- return GetWaitbase(input, output);
- case IoctlCommand::IocChannelSetTimeoutCommand:
- return ChannelSetTimeout(input, output);
- case IoctlCommand::IocChannelSetTimeslice:
- return ChannelSetTimeslice(input, output);
- default:
+NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x3:
+ return GetWaitbase(input, output);
+ default:
+ break;
+ }
+ break;
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input, output);
+ case 0x3:
+ return ChannelSetTimeout(input, output);
+ case 0x8:
+ return SubmitGPFIFOBase(input, output, false);
+ case 0x9:
+ return AllocateObjectContext(input, output);
+ case 0xb:
+ return ZCullBind(input, output);
+ case 0xc:
+ return SetErrorNotifier(input, output);
+ case 0xd:
+ return SetChannelPriority(input, output);
+ case 0x1a:
+ return AllocGPFIFOEx2(input, output);
+ case 0x1b:
+ return SubmitGPFIFOBase(input, output, true);
+ case 0x1d:
+ return ChannelSetTimeslice(input, output);
+ default:
+ break;
+ }
+ break;
+ case 'G':
+ switch (command.cmd) {
+ case 0x14:
+ return SetClientData(input, output);
+ case 0x15:
+ return GetClientData(input, output);
+ default:
+ break;
+ }
break;
}
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+};
- if (command.group == NVGPU_IOCTL_MAGIC) {
- if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
- return SubmitGPFIFO(input, output);
- }
- if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
- return KickoffPB(input, output, input2, version);
+NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 'H':
+ switch (command.cmd) {
+ case 0x1b:
+ return SubmitGPFIFOBase(input, inline_input, output);
}
+ break;
}
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
-};
+NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
-u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
user_data = params.data;
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
params.data = user_data;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&zcull_params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
zcull_params.mode);
std::memcpy(output.data(), &zcull_params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetErrorNotifier params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
params.size, params.mem);
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&channel_priority, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocGpfifoEx2 params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV,
@@ -126,15 +157,15 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
params.unk3);
- auto& gpu = system.GPU();
- params.fence_out.id = assigned_syncpoints;
- params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
- assigned_syncpoints++;
+ channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
+
+ params.fence_out = channel_fence;
+
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocObjCtx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -142,102 +173,149 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
params.obj_id = 0x0;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
- if (input.size() < sizeof(IoctlSubmitGpfifo)) {
- UNIMPLEMENTED();
+static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
+ return {
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
+ Tegra::SubmissionMode::Increasing),
+ {fence.value},
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
+ Tegra::SubmissionMode::Increasing),
+ Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id),
+ };
+}
+
+static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
+ std::vector<Tegra::CommandHeader> result{
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
+ Tegra::SubmissionMode::Increasing),
+ {}};
+
+ for (u32 count = 0; count < add_increment; ++count) {
+ result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
+ Tegra::SubmissionMode::Increasing));
+ result.emplace_back(
+ Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id));
}
- IoctlSubmitGpfifo params{};
- std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
+
+ return result;
+}
+
+static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
+ u32 add_increment) {
+ std::vector<Tegra::CommandHeader> result{
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
+ Tegra::SubmissionMode::Increasing),
+ {}};
+ const std::vector<Tegra::CommandHeader> increment{
+ BuildIncrementCommandList(fence, add_increment)};
+
+ result.insert(result.end(), increment.begin(), increment.end());
+
+ return result;
+}
+
+NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
+ Tegra::CommandList&& entries) {
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
- ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
- params.num_entries * sizeof(Tegra::CommandListHeader),
- "Incorrect input size");
+ auto& gpu = system.GPU();
- Tegra::CommandList entries(params.num_entries);
- std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
- params.num_entries * sizeof(Tegra::CommandListHeader));
+ params.fence_out.id = channel_fence.id;
- UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
- UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
+ if (params.flags.add_wait.Value() &&
+ !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
+ gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
+ }
- auto& gpu = system.GPU();
- u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
- if (params.flags.increment.Value()) {
- params.fence_out.value += current_syncpoint_value;
+ if (params.flags.add_increment.Value() || params.flags.increment.Value()) {
+ const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0;
+ params.fence_out.value = syncpoint_manager.IncreaseSyncpoint(
+ params.fence_out.id, params.AddIncrementValue() + increment_value);
} else {
- params.fence_out.value = current_syncpoint_value;
+ params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
}
+
gpu.PushGPUEntries(std::move(entries));
+ if (params.flags.add_increment.Value()) {
+ if (params.flags.suppress_wfi) {
+ gpu.PushGPUEntries(Tegra::CommandList{
+ BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())});
+ } else {
+ gpu.PushGPUEntries(Tegra::CommandList{
+ BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())});
+ }
+ }
+
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
- const std::vector<u8>& input2, IoctlVersion version) {
+NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+ bool kickoff) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
+ return NvResult::InvalidSize;
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
- LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
- params.num_entries, params.flags.raw);
-
Tegra::CommandList entries(params.num_entries);
- if (version == IoctlVersion::Version2) {
- std::memcpy(entries.data(), input2.data(),
- params.num_entries * sizeof(Tegra::CommandListHeader));
- } else {
- system.Memory().ReadBlock(params.address, entries.data(),
- params.num_entries * sizeof(Tegra::CommandListHeader));
- }
- UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
- UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
- auto& gpu = system.GPU();
- u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
- if (params.flags.increment.Value()) {
- params.fence_out.value += current_syncpoint_value;
+ if (kickoff) {
+ system.Memory().ReadBlock(params.address, entries.command_lists.data(),
+ params.num_entries * sizeof(Tegra::CommandListHeader));
} else {
- params.fence_out.value = current_syncpoint_value;
+ std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
+ params.num_entries * sizeof(Tegra::CommandListHeader));
}
- gpu.PushGPUEntries(std::move(entries));
- std::memcpy(output.data(), &params, output.size());
- return 0;
+ return SubmitGPFIFOImpl(params, output, std::move(entries));
+}
+
+NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
+ const std::vector<u8>& input_inline,
+ std::vector<u8>& output) {
+ if (input.size() < sizeof(IoctlSubmitGpfifo)) {
+ UNIMPLEMENTED();
+ return NvResult::InvalidSize;
+ }
+ IoctlSubmitGpfifo params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
+ Tegra::CommandList entries(params.num_entries);
+ std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size());
+ return SubmitGPFIFOImpl(params, output, std::move(entries));
}
-u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlChannelSetTimeout params{};
std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetTimeslice params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
channel_timeslice = params.timeslice;
- return 0;
+ return NvResult::Success;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 2ac74743f..e0298b4fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -11,46 +11,28 @@
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/nvdata.h"
+#include "video_core/dma_pusher.h"
+
+namespace Service::Nvidia {
+class SyncpointManager;
+}
namespace Service::Nvidia::Devices {
class nvmap;
-constexpr u32 NVGPU_IOCTL_MAGIC('H');
-constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
-constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
-
class nvhost_gpu final : public nvdevice {
public:
- explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager);
~nvhost_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- IocAllocGPFIFOCommand = 0x40084805,
- IocSetClientDataCommand = 0x40084714,
- IocGetClientDataCommand = 0x80084715,
- IocZCullBind = 0xc010480b,
- IocSetErrorNotifierCommand = 0xC018480C,
- IocChannelSetPriorityCommand = 0x4004480D,
- IocEnableCommand = 0x0000480E,
- IocDisableCommand = 0x0000480F,
- IocPreemptCommand = 0x00004810,
- IocForceResetCommand = 0x00004811,
- IocEventIdControlCommand = 0x40084812,
- IocGetErrorNotificationCommand = 0xC0104817,
- IocAllocGPFIFOExCommand = 0x40204818,
- IocAllocGPFIFOEx2Command = 0xC020481A,
- IocAllocObjCtxCommand = 0xC0104809,
- IocChannelGetWaitbaseCommand = 0xC0080003,
- IocChannelSetTimeoutCommand = 0x40044803,
- IocChannelSetTimeslice = 0xC004481D,
- };
-
enum class CtxObjects : u32_le {
Ctx2D = 0x902D,
Ctx3D = 0xB197,
@@ -61,63 +43,63 @@ private:
};
struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
+ s32_le nvmap_fd{};
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
struct IoctlChannelSetTimeout {
- u32_le timeout;
+ u32_le timeout{};
};
static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
struct IoctlAllocGPFIFO {
- u32_le num_entries;
- u32_le flags;
+ u32_le num_entries{};
+ u32_le flags{};
};
static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
struct IoctlClientData {
- u64_le data;
+ u64_le data{};
};
static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size");
struct IoctlZCullBind {
- u64_le gpu_va;
- u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
+ u64_le gpu_va{};
+ u32_le mode{}; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size");
struct IoctlSetErrorNotifier {
- u64_le offset;
- u64_le size;
- u32_le mem; // nvmap object handle
+ u64_le offset{};
+ u64_le size{};
+ u32_le mem{}; // nvmap object handle
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
struct IoctlChannelSetPriority {
- u32_le priority;
+ u32_le priority{};
};
static_assert(sizeof(IoctlChannelSetPriority) == 4,
"IoctlChannelSetPriority is incorrect size");
struct IoctlSetTimeslice {
- u32_le timeslice;
+ u32_le timeslice{};
};
static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size");
struct IoctlEventIdControl {
- u32_le cmd; // 0=disable, 1=enable, 2=clear
- u32_le id;
+ u32_le cmd{}; // 0=disable, 1=enable, 2=clear
+ u32_le id{};
};
static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
struct IoctlGetErrorNotification {
- u64_le timestamp;
- u32_le info32;
- u16_le info16;
- u16_le status; // always 0xFFFF
+ u64_le timestamp{};
+ u32_le info32{};
+ u16_le info16{};
+ u16_le status{}; // always 0xFFFF
};
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
@@ -125,80 +107,89 @@ private:
static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
struct IoctlAllocGpfifoEx {
- u32_le num_entries;
- u32_le flags;
- u32_le unk0;
- u32_le unk1;
- u32_le unk2;
- u32_le unk3;
- u32_le unk4;
- u32_le unk5;
+ u32_le num_entries{};
+ u32_le flags{};
+ u32_le unk0{};
+ u32_le unk1{};
+ u32_le unk2{};
+ u32_le unk3{};
+ u32_le unk4{};
+ u32_le unk5{};
};
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
struct IoctlAllocGpfifoEx2 {
- u32_le num_entries; // in
- u32_le flags; // in
- u32_le unk0; // in (1 works)
- Fence fence_out; // out
- u32_le unk1; // in
- u32_le unk2; // in
- u32_le unk3; // in
+ u32_le num_entries{}; // in
+ u32_le flags{}; // in
+ u32_le unk0{}; // in (1 works)
+ Fence fence_out{}; // out
+ u32_le unk1{}; // in
+ u32_le unk2{}; // in
+ u32_le unk3{}; // in
};
static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
struct IoctlAllocObjCtx {
- u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
- // 0xB06F=channel_gpfifo
- u32_le flags;
- u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
+ u32_le class_num{}; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
+ // 0xB06F=channel_gpfifo
+ u32_le flags{};
+ u64_le obj_id{}; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
};
static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size");
struct IoctlSubmitGpfifo {
- u64_le address; // pointer to gpfifo entry structs
- u32_le num_entries; // number of fence objects being submitted
+ u64_le address{}; // pointer to gpfifo entry structs
+ u32_le num_entries{}; // number of fence objects being submitted
union {
u32_le raw;
BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
BitField<1, 1, u32_le> add_increment; // append an increment to the list
- BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
+ BitField<2, 1, u32_le> new_hw_format; // mostly ignored
+ BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
BitField<8, 1, u32_le> increment; // increment the returned fence
} flags;
- Fence fence_out; // returned new fence object for others to wait on
+ Fence fence_out{}; // returned new fence object for others to wait on
+
+ u32 AddIncrementValue() const {
+ return flags.add_increment.Value() << 1;
+ }
};
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
"IoctlSubmitGpfifo is incorrect size");
struct IoctlGetWaitbase {
- u32 unknown; // seems to be ignored? Nintendo added this
- u32 value;
+ u32 unknown{}; // seems to be ignored? Nintendo added this
+ u32 value{};
};
static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
- u32_le nvmap_fd{};
+ s32_le nvmap_fd{};
u64_le user_data{};
IoctlZCullBind zcull_params{};
u32_le channel_priority{};
u32_le channel_timeslice{};
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
- u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
- u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
- u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
- u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
- const std::vector<u8>& input2, IoctlVersion version);
- u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
+ Tegra::CommandList&& entries);
+ NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+ bool kickoff = false);
+ NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline,
+ std::vector<u8>& output);
+ NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
std::shared_ptr<nvmap> nvmap_dev;
- u32 assigned_syncpoints{};
+ SyncpointManager& syncpoint_manager;
+ Fence channel_fence;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index bdae8b887..d8735491c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -2,39 +2,71 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
-
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
+#include "video_core/memory_manager.h"
+#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
-nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
+nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
nvhost_nvdec::~nvhost_nvdec() = default;
-u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
+NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x1:
+ return Submit(input, output);
+ case 0x2:
+ return GetSyncpoint(input, output);
+ case 0x3:
+ return GetWaitbase(input, output);
+ case 0x7:
+ return SetSubmitTimeout(input, output);
+ case 0x9:
+ return MapBuffer(input, output);
+ case 0xa: {
+ if (command.length == 0x1c) {
+ LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
+ Tegra::ChCommandHeaderList cmdlist(1);
+ cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
+ system.GPU().PushCommandBuffer(cmdlist);
+ }
+ return UnmapBuffer(input, output);
+ }
+ default:
+ break;
+ }
+ break;
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input);
+ default:
+ break;
+ }
+ break;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
- IoctlSetNvmapFD params{};
- std::memcpy(&params, input.data(), input.size());
- LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- nvmap_fd = params.nvmap_fd;
- return 0;
+NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index cbdac8069..79b8b6de1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -4,35 +4,21 @@
#pragma once
-#include <vector>
-#include "common/common_types.h"
-#include "common/swap.h"
-#include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include <memory>
+#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
namespace Service::Nvidia::Devices {
-class nvhost_nvdec final : public nvdevice {
+class nvhost_nvdec final : public nvhost_nvdec_common {
public:
- explicit nvhost_nvdec(Core::System& system);
+ explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_nvdec() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
-
-private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- };
-
- struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
- };
- static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
-
- u32_le nvmap_fd{};
-
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
new file mode 100644
index 000000000..b49cecb42
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -0,0 +1,229 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstring>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
+#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/memory.h"
+#include "video_core/memory_manager.h"
+#include "video_core/renderer_base.h"
+
+namespace Service::Nvidia::Devices {
+
+namespace {
+// Splice vectors will copy count amount of type T from the input vector into the dst vector.
+template <typename T>
+std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
+ std::size_t offset) {
+ std::memcpy(dst.data(), input.data() + offset, count * sizeof(T));
+ offset += count * sizeof(T);
+ return offset;
+}
+
+// Write vectors will write data to the output buffer
+template <typename T>
+std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) {
+ std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T));
+ offset += src.size() * sizeof(T);
+ return offset;
+}
+} // Anonymous namespace
+
+nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_nvdec_common::~nvhost_nvdec_common() = default;
+
+NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
+ IoctlSetNvmapFD params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
+ LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+
+ nvmap_fd = params.nvmap_fd;
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlSubmit params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
+ LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
+
+ // Instantiate param buffers
+ std::size_t offset = sizeof(IoctlSubmit);
+ std::vector<CommandBuffer> command_buffers(params.cmd_buffer_count);
+ std::vector<Reloc> relocs(params.relocation_count);
+ std::vector<u32> reloc_shifts(params.relocation_count);
+ std::vector<SyncptIncr> syncpt_increments(params.syncpoint_count);
+ std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
+ std::vector<Fence> fences(params.fence_count);
+
+ // Splice input into their respective buffers
+ offset = SpliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
+ offset = SpliceVectors(input, relocs, params.relocation_count, offset);
+ offset = SpliceVectors(input, reloc_shifts, params.relocation_count, offset);
+ offset = SpliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
+ offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset);
+ offset = SpliceVectors(input, fences, params.fence_count, offset);
+
+ // TODO(ameerj): For async gpu, utilize fences for syncpoint 'max' increment
+
+ auto& gpu = system.GPU();
+
+ for (const auto& cmd_buffer : command_buffers) {
+ auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
+ ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
+ const auto map = FindBufferMap(object->dma_map_addr);
+ if (!map) {
+ LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
+ object->addr, object->dma_map_addr);
+ return NvResult::Success;
+ }
+ Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
+ gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
+ cmdlist.size() * sizeof(u32));
+ gpu.PushCommandBuffer(cmdlist);
+ }
+
+ std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
+ // Some games expect command_buffers to be written back
+ offset = sizeof(IoctlSubmit);
+ offset = WriteVectors(output, command_buffers, offset);
+ offset = WriteVectors(output, relocs, offset);
+ offset = WriteVectors(output, reloc_shifts, offset);
+ offset = WriteVectors(output, syncpt_increments, offset);
+ offset = WriteVectors(output, wait_checks, offset);
+
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlGetSyncpoint params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
+ LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
+
+ // We found that implementing this causes deadlocks with async gpu, along with degraded
+ // performance. TODO: RE the nvdec async implementation
+ params.value = 0;
+ std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
+
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlGetWaitbase params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
+ params.value = 0; // Seems to be hard coded at 0
+ std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlMapBuffer params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
+ std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
+
+ SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
+
+ auto& gpu = system.GPU();
+
+ for (auto& cmf_buff : cmd_buffer_handles) {
+ auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
+ if (!object) {
+ LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::InvalidState;
+ }
+ if (object->dma_map_addr == 0) {
+ // NVDEC and VIC memory is in the 32-bit address space
+ // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
+ const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
+ object->dma_map_addr = static_cast<u32>(low_addr);
+ // Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
+ ASSERT(object->dma_map_addr == low_addr);
+ }
+ if (!object->dma_map_addr) {
+ LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
+ } else {
+ cmf_buff.map_address = object->dma_map_addr;
+ AddBufferMap(object->dma_map_addr, object->size, object->addr,
+ object->status == nvmap::Object::Status::Allocated);
+ }
+ }
+ std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
+ std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
+ cmd_buffer_handles.size() * sizeof(MapBufferEntry));
+
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlMapBuffer params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
+ std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
+ SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
+
+ auto& gpu = system.GPU();
+
+ for (auto& cmf_buff : cmd_buffer_handles) {
+ const auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
+ if (!object) {
+ LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::InvalidState;
+ }
+ if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
+ gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
+ } else {
+ // This occurs quite frequently, however does not seem to impact functionality
+ LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
+ object->dma_map_addr);
+ }
+ object->dma_map_addr = 0;
+ }
+ std::memset(output.data(), 0, output.size());
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ std::memcpy(&submit_timeout, input.data(), input.size());
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ return NvResult::Success;
+}
+
+std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
+ GPUVAddr gpu_addr) const {
+ const auto it = std::find_if(
+ buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
+ return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
+ });
+
+ ASSERT(it != buffer_mappings.end());
+ return it->second;
+}
+
+void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated) {
+ buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
+}
+
+std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
+ const auto iter{buffer_mappings.find(gpu_addr)};
+ if (iter == buffer_mappings.end()) {
+ return std::nullopt;
+ }
+ std::size_t size = 0;
+ if (iter->second.IsAllocated()) {
+ size = iter->second.Size();
+ }
+ buffer_mappings.erase(iter);
+ return size;
+}
+
+} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
new file mode 100644
index 000000000..86ba3a4d1
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -0,0 +1,196 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <vector>
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/nvdrv/devices/nvdevice.h"
+
+namespace Service::Nvidia::Devices {
+class nvmap;
+
+class nvhost_nvdec_common : public nvdevice {
+public:
+ explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ ~nvhost_nvdec_common() override;
+
+ /**
+ * Handles an ioctl1 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param output A buffer where the output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) = 0;
+
+ /**
+ * Handles an ioctl2 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
+ * @param output A buffer where the output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
+
+ /**
+ * Handles an ioctl3 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param output A buffer where the output data will be written to.
+ * @param inline_output A buffer where the inlined output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) = 0;
+
+protected:
+ class BufferMap final {
+ public:
+ constexpr BufferMap() = default;
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
+ : start_addr{start_addr}, end_addr{start_addr + size} {}
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated)
+ : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
+ is_allocated{is_allocated} {}
+
+ constexpr VAddr StartAddr() const {
+ return start_addr;
+ }
+
+ constexpr VAddr EndAddr() const {
+ return end_addr;
+ }
+
+ constexpr std::size_t Size() const {
+ return end_addr - start_addr;
+ }
+
+ constexpr VAddr CpuAddr() const {
+ return cpu_addr;
+ }
+
+ constexpr bool IsAllocated() const {
+ return is_allocated;
+ }
+
+ private:
+ GPUVAddr start_addr{};
+ GPUVAddr end_addr{};
+ VAddr cpu_addr{};
+ bool is_allocated{};
+ };
+
+ struct IoctlSetNvmapFD {
+ s32_le nvmap_fd{};
+ };
+ static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
+
+ struct IoctlSubmitCommandBuffer {
+ u32_le id{};
+ u32_le offset{};
+ u32_le count{};
+ };
+ static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
+ "IoctlSubmitCommandBuffer is incorrect size");
+ struct IoctlSubmit {
+ u32_le cmd_buffer_count{};
+ u32_le relocation_count{};
+ u32_le syncpoint_count{};
+ u32_le fence_count{};
+ };
+ static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
+
+ struct CommandBuffer {
+ s32 memory_id{};
+ u32 offset{};
+ s32 word_count{};
+ };
+ static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
+
+ struct Reloc {
+ s32 cmdbuffer_memory{};
+ s32 cmdbuffer_offset{};
+ s32 target{};
+ s32 target_offset{};
+ };
+ static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
+
+ struct SyncptIncr {
+ u32 id{};
+ u32 increments{};
+ };
+ static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
+
+ struct Fence {
+ u32 id{};
+ u32 value{};
+ };
+ static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
+
+ struct IoctlGetSyncpoint {
+ // Input
+ u32_le param{};
+ // Output
+ u32_le value{};
+ };
+ static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
+
+ struct IoctlGetWaitbase {
+ u32_le unknown{}; // seems to be ignored? Nintendo added this
+ u32_le value{};
+ };
+ static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
+
+ struct IoctlMapBuffer {
+ u32_le num_entries{};
+ u32_le data_address{}; // Ignored by the driver.
+ u32_le attach_host_ch_das{};
+ };
+ static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
+
+ struct IocGetIdParams {
+ // Input
+ u32_le param{};
+ // Output
+ u32_le value{};
+ };
+ static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
+
+ // Used for mapping and unmapping command buffers
+ struct MapBufferEntry {
+ u32_le map_handle{};
+ u32_le map_address{};
+ };
+ static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
+
+ /// Ioctl command implementations
+ NvResult SetNVMAPfd(const std::vector<u8>& input);
+ NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+
+ std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
+ void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
+ std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
+
+ s32_le nvmap_fd{};
+ u32_le submit_timeout{};
+ std::shared_ptr<nvmap> nvmap_dev;
+
+ // This is expected to be ordered, therefore we must use a map, not unordered_map
+ std::map<GPUVAddr, BufferMap> buffer_mappings;
+};
+}; // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 96e7b7dab..2d06955c0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,28 +13,44 @@ namespace Service::Nvidia::Devices {
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
nvhost_nvjpg::~nvhost_nvjpg() = default;
-u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
+NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input, output);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
- return 0;
+ return NvResult::Success;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 98dcac52f..43948d18d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,23 +16,21 @@ public:
explicit nvhost_nvjpg(Core::System& system);
~nvhost_nvjpg() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- };
-
struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
+ s32_le nvmap_fd{};
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
- u32_le nvmap_fd{};
+ s32_le nvmap_fd{};
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index c695b8863..805fe86ae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -2,39 +2,63 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
-
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
+#include "video_core/memory_manager.h"
+#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
+nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
-nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
nvhost_vic::~nvhost_vic() = default;
-u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
+NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x1:
+ return Submit(input, output);
+ case 0x2:
+ return GetSyncpoint(input, output);
+ case 0x3:
+ return GetWaitbase(input, output);
+ case 0x9:
+ return MapBuffer(input, output);
+ case 0xa:
+ return UnmapBuffer(input, output);
+ default:
+ break;
+ }
+ break;
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
- IoctlSetNvmapFD params{};
- std::memcpy(&params, input.data(), input.size());
- LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- nvmap_fd = params.nvmap_fd;
- return 0;
+NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index bec32bea1..b2e11f4d4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -4,35 +4,20 @@
#pragma once
-#include <vector>
-#include "common/common_types.h"
-#include "common/swap.h"
-#include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
namespace Service::Nvidia::Devices {
+class nvmap;
-class nvhost_vic final : public nvdevice {
+class nvhost_vic final : public nvhost_nvdec_common {
public:
- explicit nvhost_vic(Core::System& system);
- ~nvhost_vic() override;
-
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
-
-private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- };
-
- struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
- };
- static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
-
- u32_le nvmap_fd{};
-
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ ~nvhost_vic();
+
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
};
-
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 8c742316c..4015a2740 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,16 +11,54 @@
namespace Service::Nvidia::Devices {
-namespace NvErrCodes {
-enum {
- OperationNotPermitted = -1,
- InvalidValue = -22,
-};
+nvmap::nvmap(Core::System& system) : nvdevice(system) {
+ // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
+ // represent this.
+ CreateObject(0);
}
-nvmap::nvmap(Core::System& system) : nvdevice(system) {}
nvmap::~nvmap() = default;
+NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x1:
+ switch (command.cmd) {
+ case 0x1:
+ return IocCreate(input, output);
+ case 0x3:
+ return IocFromId(input, output);
+ case 0x4:
+ return IocAlloc(input, output);
+ case 0x5:
+ return IocFree(input, output);
+ case 0x9:
+ return IocParam(input, output);
+ case 0xe:
+ return IocGetId(input, output);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
VAddr nvmap::GetObjectAddress(u32 handle) const {
auto object = GetObject(handle);
ASSERT(object);
@@ -28,66 +66,50 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
return object->addr;
}
-u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::Create:
- return IocCreate(input, output);
- case IoctlCommand::Alloc:
- return IocAlloc(input, output);
- case IoctlCommand::GetId:
- return IocGetId(input, output);
- case IoctlCommand::FromId:
- return IocFromId(input, output);
- case IoctlCommand::Param:
- return IocParam(input, output);
- case IoctlCommand::Free:
- return IocFree(input, output);
- }
+u32 nvmap::CreateObject(u32 size) {
+ // Create a new nvmap object and obtain a handle to it.
+ auto object = std::make_shared<Object>();
+ object->id = next_id++;
+ object->size = size;
+ object->status = Object::Status::Created;
+ object->refcount = 1;
+
+ const u32 handle = next_handle++;
+
+ handles.insert_or_assign(handle, std::move(object));
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ return handle;
}
-u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult 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) {
LOG_ERROR(Service_NVDRV, "Size is 0");
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
- // Create a new nvmap object and obtain a handle to it.
- auto object = std::make_shared<Object>();
- object->id = next_id++;
- object->size = params.size;
- object->status = Object::Status::Created;
- object->refcount = 1;
-
- u32 handle = next_handle++;
- handles[handle] = std::move(object);
- params.handle = handle;
+ params.handle = CreateObject(params.size);
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult 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) {
LOG_ERROR(Service_NVDRV, "Handle is 0");
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if ((params.align - 1) & params.align) {
LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
const u32 min_alignment = 0x1000;
@@ -98,12 +120,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if (object->status == Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ return NvResult::InsufficientMemory;
}
object->flags = params.flags;
@@ -113,10 +135,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
object->status = Object::Status::Allocated;
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -124,22 +146,22 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
if (!params.handle) {
LOG_ERROR(Service_NVDRV, "Handle is zero");
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ return NvResult::BadValue;
}
params.id = object->id;
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
IocFromIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -149,13 +171,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
[&](const auto& entry) { return entry.second->id == params.id; });
if (itr == handles.end()) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
auto& object = itr->second;
if (object->status != Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
itr->second->refcount++;
@@ -164,10 +186,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
params.handle = itr->first;
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
IocParamParams params;
@@ -178,12 +200,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if (object->status != Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ return NvResult::BadValue;
}
switch (static_cast<ParamTypes>(params.param)) {
@@ -205,10 +227,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
}
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
// TODO(Subv): These flags are unconfirmed.
enum FreeFlags {
Freed = 0,
@@ -223,14 +245,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = handles.find(params.handle);
if (itr == handles.end()) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if (!itr->second->refcount) {
LOG_ERROR(
Service_NVDRV,
"There is no references to this object. The object is already freed. handle={:08X}",
params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
itr->second->refcount--;
@@ -250,7 +272,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
handles.erase(params.handle);
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 73c2e8809..4484bd79f 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -19,13 +19,15 @@ public:
explicit nvmap(Core::System& system);
~nvmap() override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
+
/// Returns the allocated address of an nvmap object given its handle.
VAddr GetObjectAddress(u32 handle) const;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
-
/// Represents an nvmap object.
struct Object {
enum class Status { Created, Allocated };
@@ -37,6 +39,7 @@ public:
VAddr addr;
Status status;
u32 refcount;
+ u32 dma_map_addr;
};
std::shared_ptr<Object> GetObject(u32 handle) const {
@@ -49,82 +52,76 @@ public:
private:
/// Id to use for the next handle that is created.
- u32 next_handle = 1;
+ u32 next_handle = 0;
/// Id to use for the next object that is created.
- u32 next_id = 1;
+ u32 next_id = 0;
/// Mapping of currently allocated handles to the objects they represent.
std::unordered_map<u32, std::shared_ptr<Object>> handles;
- enum class IoctlCommand : u32 {
- Create = 0xC0080101,
- FromId = 0xC0080103,
- Alloc = 0xC0200104,
- Free = 0xC0180105,
- Param = 0xC00C0109,
- GetId = 0xC008010E,
- };
struct IocCreateParams {
// Input
- u32_le size;
+ u32_le size{};
// Output
- u32_le handle;
+ u32_le handle{};
};
static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
struct IocFromIdParams {
// Input
- u32_le id;
+ u32_le id{};
// Output
- u32_le handle;
+ u32_le handle{};
};
static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
struct IocAllocParams {
// Input
- u32_le handle;
- u32_le heap_mask;
- u32_le flags;
- u32_le align;
- u8 kind;
+ u32_le handle{};
+ u32_le heap_mask{};
+ u32_le flags{};
+ u32_le align{};
+ u8 kind{};
INSERT_PADDING_BYTES(7);
- u64_le addr;
+ u64_le addr{};
};
static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
struct IocFreeParams {
- u32_le handle;
+ u32_le handle{};
INSERT_PADDING_BYTES(4);
- u64_le address;
- u32_le size;
- u32_le flags;
+ u64_le address{};
+ u32_le size{};
+ u32_le flags{};
};
static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
struct IocParamParams {
// Input
- u32_le handle;
- u32_le param;
+ u32_le handle{};
+ u32_le param{};
// Output
- u32_le result;
+ u32_le result{};
};
static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
struct IocGetIdParams {
// Output
- u32_le id;
+ u32_le id{};
// Input
- u32_le handle;
+ u32_le handle{};
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
- u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output);
+ u32 CreateObject(u32 size);
+
+ NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index c8ea6c661..f6c38e853 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -23,138 +23,184 @@ void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
const auto& buffer = ctx.ReadBuffer();
- std::string device_name(buffer.begin(), buffer.end());
+ const std::string device_name(buffer.begin(), buffer.end());
+ DeviceFD fd = nvdrv->Open(device_name);
- u32 fd = nvdrv->Open(device_name);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(fd);
- rb.Push<u32>(0);
+ rb.Push<DeviceFD>(fd);
+ rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
+}
+
+void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(result);
}
-void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
+void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u32 fd = rp.Pop<u32>();
- u32 command = rp.Pop<u32>();
-
- /// Ioctl 3 has 2 outputs, first in the input params, second is the result
- std::vector<u8> output(ctx.GetWriteBufferSize(0));
- std::vector<u8> output2;
- if (version == IoctlVersion::Version3) {
- output2.resize((ctx.GetWriteBufferSize(1)));
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto command = rp.PopRaw<Ioctl>();
+ LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
}
- /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
- /// KickOfPB uses this
- auto input = ctx.ReadBuffer(0);
+ // Check device
+ std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+ const auto input_buffer = ctx.ReadBuffer(0);
- std::vector<u8> input2;
- if (version == IoctlVersion::Version2) {
- input2 = ctx.ReadBuffer(1);
- }
+ const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
- IoctlCtrl ctrl{};
-
- u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
-
- if (ctrl.must_delay) {
- ctrl.fresh_call = false;
- ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout,
- [=](std::shared_ptr<Kernel::Thread> thread,
- Kernel::HLERequestContext& ctx,
- Kernel::ThreadWakeupReason reason) {
- IoctlCtrl ctrl2{ctrl};
- std::vector<u8> tmp_output = output;
- std::vector<u8> tmp_output2 = output2;
- u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
- tmp_output2, ctrl2, version);
- ctx.WriteBuffer(tmp_output, 0);
- if (version == IoctlVersion::Version3) {
- ctx.WriteBuffer(tmp_output2, 1);
- }
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push(result);
- },
- nvdrv->GetEventWriteable(ctrl.event_id));
- } else {
- ctx.WriteBuffer(output);
- if (version == IoctlVersion::Version3) {
- ctx.WriteBuffer(output2, 1);
- }
+ if (command.is_out != 0) {
+ ctx.WriteBuffer(output_buffer);
}
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(result);
-}
-
-void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NVDRV, "called");
- IoctlBase(ctx, IoctlVersion::Version1);
+ rb.PushEnum(nv_result);
}
void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NVDRV, "called");
- IoctlBase(ctx, IoctlVersion::Version2);
+ IPC::RequestParser rp{ctx};
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto command = rp.PopRaw<Ioctl>();
+ LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
+ const auto input_buffer = ctx.ReadBuffer(0);
+ const auto input_inlined_buffer = ctx.ReadBuffer(1);
+ std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+
+ const auto nv_result =
+ nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
+
+ if (command.is_out != 0) {
+ ctx.WriteBuffer(output_buffer);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(nv_result);
}
void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NVDRV, "called");
- IoctlBase(ctx, IoctlVersion::Version3);
+ IPC::RequestParser rp{ctx};
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto command = rp.PopRaw<Ioctl>();
+ LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
+ const auto input_buffer = ctx.ReadBuffer(0);
+ std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+ std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
+
+ const auto nv_result =
+ nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
+
+ if (command.is_out != 0) {
+ ctx.WriteBuffer(output_buffer, 0);
+ ctx.WriteBuffer(output_buffer_inline, 1);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(nv_result);
}
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
- IPC::RequestParser rp{ctx};
- u32 fd = rp.Pop<u32>();
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
- auto result = nvdrv->Close(fd);
+ IPC::RequestParser rp{ctx};
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto result = nvdrv->Close(fd);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(result);
}
void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ is_initialized = true;
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
+ rb.PushEnum(NvResult::Success);
}
void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u32 fd = rp.Pop<u32>();
- // TODO(Blinkhawk): Figure the meaning of the flag at bit 16
- u32 event_id = rp.Pop<u32>() & 0x000000FF;
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto event_id = rp.Pop<u32>() & 0x00FF;
LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(RESULT_SUCCESS);
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
+ const auto nv_result = nvdrv->VerifyFD(fd);
+ if (nv_result != NvResult::Success) {
+ LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
+ ServiceError(ctx, nv_result);
+ return;
+ }
+
if (event_id < MaxNvEvents) {
+ IPC::ResponseBuilder rb{ctx, 3, 1};
+ rb.Push(RESULT_SUCCESS);
auto event = nvdrv->GetEvent(event_id);
event->Clear();
rb.PushCopyObjects(event);
- rb.Push<u32>(NvResult::Success);
+ rb.PushEnum(NvResult::Success);
} else {
- rb.Push<u32>(0);
- rb.Push<u32>(NvResult::BadParameter);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(NvResult::BadParameter);
}
}
-void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
+void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
pid = rp.Pop<u64>();
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
+ rb.PushEnum(NvResult::Success);
}
-void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
+void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
@@ -164,8 +210,9 @@ void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(NvResult::Success);
}
void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
@@ -181,19 +228,20 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
: ServiceFramework(name), nvdrv(std::move(nvdrv)) {
static const FunctionInfo functions[] = {
{0, &NVDRV::Open, "Open"},
- {1, &NVDRV::Ioctl, "Ioctl"},
+ {1, &NVDRV::Ioctl1, "Ioctl"},
{2, &NVDRV::Close, "Close"},
{3, &NVDRV::Initialize, "Initialize"},
{4, &NVDRV::QueryEvent, "QueryEvent"},
{5, nullptr, "MapSharedMem"},
{6, &NVDRV::GetStatus, "GetStatus"},
- {7, nullptr, "ForceSetClientPID"},
- {8, &NVDRV::SetClientPID, "SetClientPID"},
+ {7, nullptr, "SetAruidForTest"},
+ {8, &NVDRV::SetAruid, "SetAruid"},
{9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
{10, nullptr, "InitializeDevtools"},
{11, &NVDRV::Ioctl2, "Ioctl2"},
{12, &NVDRV::Ioctl3, "Ioctl3"},
- {13, &NVDRV::FinishInitialize, "FinishInitialize"},
+ {13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled,
+ "SetGraphicsFirmwareMemoryMarginEnabled"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 9269ce00c..e05f905ae 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -23,21 +23,23 @@ public:
private:
void Open(Kernel::HLERequestContext& ctx);
- void Ioctl(Kernel::HLERequestContext& ctx);
+ void Ioctl1(Kernel::HLERequestContext& ctx);
void Ioctl2(Kernel::HLERequestContext& ctx);
void Ioctl3(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
void Initialize(Kernel::HLERequestContext& ctx);
void QueryEvent(Kernel::HLERequestContext& ctx);
- void SetClientPID(Kernel::HLERequestContext& ctx);
- void FinishInitialize(Kernel::HLERequestContext& ctx);
+ void SetAruid(Kernel::HLERequestContext& ctx);
+ void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
void GetStatus(Kernel::HLERequestContext& ctx);
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
- void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
+
+ void ServiceError(Kernel::HLERequestContext& ctx, NvResult result);
std::shared_ptr<Module> nvdrv;
u64 pid{};
+ bool is_initialized{};
};
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 529b03471..3294bc0e7 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,12 +1,16 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_types.h"
namespace Service::Nvidia {
constexpr u32 MaxSyncPoints = 192;
constexpr u32 MaxNvEvents = 64;
+using DeviceFD = s32;
+
+constexpr DeviceFD INVALID_NVDRV_FD = -1;
struct Fence {
s32 id;
@@ -20,11 +24,61 @@ struct MultiFence {
std::array<Fence, 4> fences;
};
-enum NvResult : u32 {
- Success = 0,
- BadParameter = 4,
- Timeout = 5,
- ResourceError = 15,
+enum class NvResult : u32 {
+ Success = 0x0,
+ NotImplemented = 0x1,
+ NotSupported = 0x2,
+ NotInitialized = 0x3,
+ BadParameter = 0x4,
+ Timeout = 0x5,
+ InsufficientMemory = 0x6,
+ ReadOnlyAttribute = 0x7,
+ InvalidState = 0x8,
+ InvalidAddress = 0x9,
+ InvalidSize = 0xA,
+ BadValue = 0xB,
+ AlreadyAllocated = 0xD,
+ Busy = 0xE,
+ ResourceError = 0xF,
+ CountMismatch = 0x10,
+ OverFlow = 0x11,
+ InsufficientTransferMemory = 0x1000,
+ InsufficientVideoMemory = 0x10000,
+ BadSurfaceColorScheme = 0x10001,
+ InvalidSurface = 0x10002,
+ SurfaceNotSupported = 0x10003,
+ DispInitFailed = 0x20000,
+ DispAlreadyAttached = 0x20001,
+ DispTooManyDisplays = 0x20002,
+ DispNoDisplaysAttached = 0x20003,
+ DispModeNotSupported = 0x20004,
+ DispNotFound = 0x20005,
+ DispAttachDissallowed = 0x20006,
+ DispTypeNotSupported = 0x20007,
+ DispAuthenticationFailed = 0x20008,
+ DispNotAttached = 0x20009,
+ DispSamePwrState = 0x2000A,
+ DispEdidFailure = 0x2000B,
+ DispDsiReadAckError = 0x2000C,
+ DispDsiReadInvalidResp = 0x2000D,
+ FileWriteFailed = 0x30000,
+ FileReadFailed = 0x30001,
+ EndOfFile = 0x30002,
+ FileOperationFailed = 0x30003,
+ DirOperationFailed = 0x30004,
+ EndOfDirList = 0x30005,
+ ConfigVarNotFound = 0x30006,
+ InvalidConfigVar = 0x30007,
+ LibraryNotFound = 0x30008,
+ SymbolNotFound = 0x30009,
+ MemoryMapFailed = 0x3000A,
+ IoctlFailed = 0x3000F,
+ AccessDenied = 0x30010,
+ DeviceNotFound = 0x30011,
+ KernelDriverNotFound = 0x30012,
+ FileNotFound = 0x30013,
+ PathAlreadyExists = 0x30014,
+ ModuleNotPresent = 0xA000E,
};
enum class EventState {
@@ -34,21 +88,13 @@ enum class EventState {
Busy = 3,
};
-enum class IoctlVersion : u32 {
- Version1,
- Version2,
- Version3,
-};
-
-struct IoctlCtrl {
- // First call done to the servioce for services that call itself again after a call.
- bool fresh_call{true};
- // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
- bool must_delay{};
- // Timeout for the delay
- s64 timeout{};
- // NV Event Id
- s32 event_id{-1};
+union Ioctl {
+ u32_le raw;
+ BitField<0, 8, u32> cmd;
+ BitField<8, 8, u32> group;
+ BitField<16, 14, u32> length;
+ BitField<30, 1, u32> is_in;
+ BitField<31, 1, u32> is_out;
};
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 197c77db0..bdbbedd0d 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -5,6 +5,7 @@
#include <utility>
#include <fmt/format.h>
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
@@ -21,6 +22,7 @@
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvmemp.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service::Nvidia {
@@ -36,58 +38,125 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
nvflinger.SetNVDrvInstance(module_);
}
-Module::Module(Core::System& system) {
+Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
auto& kernel = system.Kernel();
for (u32 i = 0; i < MaxNvEvents; i++) {
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
- events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label);
+ events_interface.events[i] = {Kernel::WritableEvent::CreateEventPair(kernel, event_label)};
events_interface.status[i] = EventState::Free;
events_interface.registered[i] = false;
}
auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
- devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev);
+ devices["/dev/nvhost-gpu"] =
+ std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager);
devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
devices["/dev/nvmap"] = nvmap_dev;
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
- devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
- devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system);
+ devices["/dev/nvhost-ctrl"] =
+ std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager);
+ devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev);
devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
- devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system);
+ devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev);
}
Module::~Module() = default;
-u32 Module::Open(const std::string& device_name) {
- ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}",
- device_name);
+NvResult Module::VerifyFD(DeviceFD fd) const {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ if (open_files.find(fd) == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return NvResult::Success;
+}
+
+DeviceFD Module::Open(const std::string& device_name) {
+ if (devices.find(device_name) == devices.end()) {
+ LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
+ return INVALID_NVDRV_FD;
+ }
auto device = devices[device_name];
- const u32 fd = next_fd++;
+ const DeviceFD fd = next_fd++;
open_files[fd] = std::move(device);
return fd;
}
-u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- auto itr = open_files.find(fd);
- ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
+NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
- auto& device = itr->second;
- return device->ioctl({command}, input, input2, output, output2, ctrl, version);
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return itr->second->Ioctl1(command, input, output);
}
-ResultCode Module::Close(u32 fd) {
- auto itr = open_files.find(fd);
- ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
+NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return itr->second->Ioctl2(command, input, inline_input, output);
+}
+
+NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output, std::vector<u8>& inline_output) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return itr->second->Ioctl3(command, input, output, inline_output);
+}
+
+NvResult Module::Close(DeviceFD fd) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
open_files.erase(itr);
- // TODO(flerovium): return correct result code if operation failed.
- return RESULT_SUCCESS;
+ return NvResult::Success;
}
void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
@@ -95,17 +164,17 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
if (events_interface.assigned_syncpt[i] == syncpoint_id &&
events_interface.assigned_value[i] == value) {
events_interface.LiberateEvent(i);
- events_interface.events[i].writable->Signal();
+ events_interface.events[i].event.writable->Signal();
}
}
}
std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
- return events_interface.events[event_id].readable;
+ return events_interface.events[event_id].event.readable;
}
std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
- return events_interface.events[event_id].writable;
+ return events_interface.events[event_id].event.writable;
}
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index d7a1bef91..7654bb026 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -22,15 +23,23 @@ class NVFlinger;
namespace Service::Nvidia {
+class SyncpointManager;
+
namespace Devices {
class nvdevice;
}
+/// Represents an Nvidia event
+struct NvEvent {
+ Kernel::EventPair event;
+ Fence fence{};
+};
+
struct EventInterface {
// Mask representing currently busy events
u64 events_mask{};
// Each kernel event associated to an NV event
- std::array<Kernel::EventPair, MaxNvEvents> events;
+ std::array<NvEvent, MaxNvEvents> events;
// The status of the current NVEvent
std::array<EventState, MaxNvEvents> status{};
// Tells if an NVEvent is registered or not
@@ -54,7 +63,7 @@ struct EventInterface {
}
mask = mask >> 1;
}
- return {};
+ return std::nullopt;
}
void SetEventStatus(const u32 event_id, EventState new_status) {
EventState old_status = status[event_id];
@@ -103,14 +112,23 @@ public:
return std::static_pointer_cast<T>(itr->second);
}
+ NvResult VerifyFD(DeviceFD fd) const;
+
/// Opens a device node and returns a file descriptor to it.
- u32 Open(const std::string& device_name);
+ DeviceFD Open(const std::string& device_name);
+
/// Sends an ioctl command to the specified file descriptor.
- u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version);
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output);
+
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output);
+
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output, std::vector<u8>& inline_output);
+
/// Closes a device file descriptor and returns operation success.
- ResultCode Close(u32 fd);
+ NvResult Close(DeviceFD fd);
void SignalSyncpt(const u32 syncpoint_id, const u32 value);
@@ -119,11 +137,14 @@ public:
std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
private:
+ /// Manages syncpoints on the host
+ SyncpointManager syncpoint_manager;
+
/// Id to use for the next open file descriptor.
- u32 next_fd = 1;
+ DeviceFD next_fd = 1;
/// Mapping of file descriptors to the devices they reference.
- std::unordered_map<u32, std::shared_ptr<Devices::nvdevice>> open_files;
+ std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files;
/// Mapping of device node names to their implementation.
std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp
index b7b8b7a1b..73b37e805 100644
--- a/src/core/hle/service/nvdrv/nvmemp.cpp
+++ b/src/core/hle/service/nvdrv/nvmemp.cpp
@@ -10,19 +10,19 @@ namespace Service::Nvidia {
NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
static const FunctionInfo functions[] = {
- {0, &NVMEMP::Cmd0, "Cmd0"},
- {1, &NVMEMP::Cmd1, "Cmd1"},
+ {0, &NVMEMP::Open, "Open"},
+ {1, &NVMEMP::GetAruid, "GetAruid"},
};
RegisterHandlers(functions);
}
NVMEMP::~NVMEMP() = default;
-void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) {
+void NVMEMP::Open(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED();
}
-void NVMEMP::Cmd1(Kernel::HLERequestContext& ctx) {
+void NVMEMP::GetAruid(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED();
}
diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h
index 6eafb1346..c453ee4db 100644
--- a/src/core/hle/service/nvdrv/nvmemp.h
+++ b/src/core/hle/service/nvdrv/nvmemp.h
@@ -14,8 +14,8 @@ public:
~NVMEMP() override;
private:
- void Cmd0(Kernel::HLERequestContext& ctx);
- void Cmd1(Kernel::HLERequestContext& ctx);
+ void Open(Kernel::HLERequestContext& ctx);
+ void GetAruid(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
new file mode 100644
index 000000000..0151a03b7
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
@@ -0,0 +1,39 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
+#include "video_core/gpu.h"
+
+namespace Service::Nvidia {
+
+SyncpointManager::SyncpointManager(Tegra::GPU& gpu) : gpu{gpu} {}
+
+SyncpointManager::~SyncpointManager() = default;
+
+u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
+ syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
+ return GetSyncpointMin(syncpoint_id);
+}
+
+u32 SyncpointManager::AllocateSyncpoint() {
+ for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
+ if (!syncpoints[syncpoint_id].is_allocated) {
+ syncpoints[syncpoint_id].is_allocated = true;
+ return syncpoint_id;
+ }
+ }
+ UNREACHABLE_MSG("No more available syncpoints!");
+ return {};
+}
+
+u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
+ for (u32 index = 0; index < value; ++index) {
+ syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ return GetSyncpointMax(syncpoint_id);
+}
+
+} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h
new file mode 100644
index 000000000..4168b6c7e
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.h
@@ -0,0 +1,85 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <atomic>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Tegra {
+class GPU;
+}
+
+namespace Service::Nvidia {
+
+class SyncpointManager final {
+public:
+ explicit SyncpointManager(Tegra::GPU& gpu);
+ ~SyncpointManager();
+
+ /**
+ * Returns true if the specified syncpoint is expired for the given value.
+ * @param syncpoint_id Syncpoint ID to check.
+ * @param value Value to check against the specified syncpoint.
+ * @returns True if the specified syncpoint is expired for the given value, otherwise False.
+ */
+ bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
+ return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
+ }
+
+ /**
+ * Gets the lower bound for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to get the lower bound for.
+ * @returns The lower bound for the specified syncpoint.
+ */
+ u32 GetSyncpointMin(u32 syncpoint_id) const {
+ return syncpoints[syncpoint_id].min.load(std::memory_order_relaxed);
+ }
+
+ /**
+ * Gets the uper bound for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to get the upper bound for.
+ * @returns The upper bound for the specified syncpoint.
+ */
+ u32 GetSyncpointMax(u32 syncpoint_id) const {
+ return syncpoints[syncpoint_id].max.load(std::memory_order_relaxed);
+ }
+
+ /**
+ * Refreshes the minimum value for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to be refreshed.
+ * @returns The new syncpoint minimum value.
+ */
+ u32 RefreshSyncpoint(u32 syncpoint_id);
+
+ /**
+ * Allocates a new syncoint.
+ * @returns The syncpoint ID for the newly allocated syncpoint.
+ */
+ u32 AllocateSyncpoint();
+
+ /**
+ * Increases the maximum value for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to be increased.
+ * @param value Value to increase the specified syncpoint by.
+ * @returns The new syncpoint maximum value.
+ */
+ u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
+
+private:
+ struct Syncpoint {
+ std::atomic<u32> min;
+ std::atomic<u32> max;
+ std::atomic<bool> is_allocated;
+ };
+
+ std::array<Syncpoint, MaxSyncPoints> syncpoints{};
+
+ Tegra::GPU& gpu;
+};
+
+} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index f1e3d832a..b89a2d41b 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -24,13 +24,17 @@ BufferQueue::~BufferQueue() = default;
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
- Buffer buffer{};
- buffer.slot = slot;
- buffer.igbp_buffer = igbp_buffer;
- buffer.status = Buffer::Status::Free;
free_buffers.push_back(slot);
+ queue.push_back({
+ .slot = slot,
+ .status = Buffer::Status::Free,
+ .igbp_buffer = igbp_buffer,
+ .transform = {},
+ .crop_rect = {},
+ .swap_interval = 0,
+ .multi_fence = {},
+ });
- queue.emplace_back(buffer);
buffer_wait_event.writable->Signal();
}
@@ -38,7 +42,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
u32 height) {
if (free_buffers.empty()) {
- return {};
+ return std::nullopt;
}
auto f_itr = free_buffers.begin();
@@ -69,7 +73,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
}
if (itr == queue.end()) {
- return {};
+ return std::nullopt;
}
itr->status = Buffer::Status::Dequeued;
@@ -99,18 +103,33 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
queue_sequence.push_back(slot);
}
+void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
+ const auto itr = std::find_if(queue.begin(), queue.end(),
+ [slot](const Buffer& buffer) { return buffer.slot == slot; });
+ ASSERT(itr != queue.end());
+ ASSERT(itr->status != Buffer::Status::Free);
+ itr->status = Buffer::Status::Free;
+ itr->multi_fence = multi_fence;
+ itr->swap_interval = 0;
+
+ free_buffers.push_back(slot);
+
+ buffer_wait_event.writable->Signal();
+}
+
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
auto itr = queue.end();
// Iterate to find a queued buffer matching the requested slot.
while (itr == queue.end() && !queue_sequence.empty()) {
- u32 slot = queue_sequence.front();
+ const u32 slot = queue_sequence.front();
itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
});
queue_sequence.pop_front();
}
- if (itr == queue.end())
- return {};
+ if (itr == queue.end()) {
+ return std::nullopt;
+ }
itr->status = Buffer::Status::Acquired;
return *itr;
}
@@ -138,9 +157,7 @@ u32 BufferQueue::Query(QueryType type) {
switch (type) {
case QueryType::NativeWindowFormat:
- // TODO(Subv): Use an enum for this
- static constexpr u32 FormatABGR8 = 1;
- return FormatABGR8;
+ return static_cast<u32>(PixelFormat::RGBA8888);
}
UNIMPLEMENTED();
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index d5f31e567..e7517c7e1 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -66,6 +66,16 @@ public:
Rotate270 = 0x07,
};
+ enum class PixelFormat : u32 {
+ RGBA8888 = 1,
+ RGBX8888 = 2,
+ RGB888 = 3,
+ RGB565 = 4,
+ BGRA8888 = 5,
+ RGBA5551 = 6,
+ RRGBA4444 = 7,
+ };
+
struct Buffer {
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
@@ -85,6 +95,7 @@ public:
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect, u32 swap_interval,
Service::Nvidia::MultiFence& multi_fence);
+ void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
void ReleaseBuffer(u32 slot);
void Disconnect();
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 437bc5dee..44aa2bdae 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
+#include "common/thread.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
@@ -27,8 +28,34 @@
namespace Service::NVFlinger {
-constexpr s64 frame_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60);
-constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 30);
+constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
+
+void NVFlinger::VSyncThread(NVFlinger& nv_flinger) {
+ nv_flinger.SplitVSync();
+}
+
+void NVFlinger::SplitVSync() {
+ system.RegisterHostThread();
+ std::string name = "yuzu:VSyncThread";
+ MicroProfileOnThreadCreate(name.c_str());
+ Common::SetCurrentThreadName(name.c_str());
+ Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
+ s64 delay = 0;
+ while (is_running) {
+ guard->lock();
+ const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count();
+ Compose();
+ const auto ticks = GetNextTicks();
+ const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count();
+ const s64 time_passed = time_end - time_start;
+ const s64 next_time = std::max<s64>(0, ticks - time_passed - delay);
+ guard->unlock();
+ if (next_time > 0) {
+ wait_event->WaitFor(std::chrono::nanoseconds{next_time});
+ }
+ delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time;
+ }
+}
NVFlinger::NVFlinger(Core::System& system) : system(system) {
displays.emplace_back(0, "Default", system);
@@ -36,22 +63,40 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
displays.emplace_back(2, "Edid", system);
displays.emplace_back(3, "Internal", system);
displays.emplace_back(4, "Null", system);
+ guard = std::make_shared<std::mutex>();
// Schedule the screen composition events
- composition_event =
- Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) {
+ composition_event = Core::Timing::CreateEvent(
+ "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
+ const auto guard = Lock();
Compose();
- const auto ticks =
- Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
- this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late),
- composition_event);
+
+ const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
+ const auto ticks_delta = ticks - ns_late;
+ const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
+
+ this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
});
- system.CoreTiming().ScheduleEvent(frame_ticks, composition_event);
+ if (system.IsMulticore()) {
+ is_running = true;
+ wait_event = std::make_unique<Common::Event>();
+ vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this));
+ } else {
+ system.CoreTiming().ScheduleEvent(frame_ns, composition_event);
+ }
}
NVFlinger::~NVFlinger() {
- system.CoreTiming().UnscheduleEvent(composition_event, 0);
+ if (system.IsMulticore()) {
+ is_running = false;
+ wait_event->Set();
+ vsync_thread->join();
+ vsync_thread.reset();
+ wait_event.reset();
+ } else {
+ system.CoreTiming().UnscheduleEvent(composition_event, 0);
+ }
}
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -69,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
[&](const VI::Display& display) { return display.GetName() == name; });
if (itr == displays.end()) {
- return {};
+ return std::nullopt;
}
return itr->GetID();
@@ -79,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
- return {};
+ return std::nullopt;
}
const u64 layer_id = next_layer_id++;
@@ -99,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
const auto* const layer = FindLayer(display_id, layer_id);
if (layer == nullptr) {
- return {};
+ return std::nullopt;
}
return layer->GetBufferQueue().GetId();
@@ -197,12 +242,18 @@ void NVFlinger::Compose() {
const auto& igbp_buffer = buffer->get().igbp_buffer;
+ if (!system.IsPoweredOn()) {
+ return; // We are likely shutting down
+ }
+
auto& gpu = system.GPU();
const auto& multi_fence = buffer->get().multi_fence;
+ guard->unlock();
for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
const auto& fence = multi_fence.fences[fence_id];
gpu.WaitFence(fence.id, fence.value);
}
+ guard->lock();
MicroProfileFlip();
@@ -223,7 +274,7 @@ void NVFlinger::Compose() {
s64 NVFlinger::GetNextTicks() const {
constexpr s64 max_hertz = 120LL;
- return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz;
+ return (1000000000 * (1LL << swap_interval)) / max_hertz;
}
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 57a21f33b..1ebe949c0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -4,15 +4,22 @@
#pragma once
+#include <atomic>
#include <memory>
+#include <mutex>
#include <optional>
#include <string>
#include <string_view>
+#include <thread>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
+namespace Common {
+class Event;
+} // namespace Common
+
namespace Core::Timing {
class CoreTiming;
struct EventType;
@@ -47,12 +54,12 @@ public:
/// Opens the specified display and returns the ID.
///
/// If an invalid display name is provided, then an empty optional is returned.
- std::optional<u64> OpenDisplay(std::string_view name);
+ [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
/// Creates a layer on the specified display and returns the layer ID.
///
/// If an invalid display ID is specified, then an empty optional is returned.
- std::optional<u64> CreateLayer(u64 display_id);
+ [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
/// Closes a layer on all displays for the given layer ID.
void CloseLayer(u64 layer_id);
@@ -60,37 +67,45 @@ public:
/// Finds the buffer queue ID of the specified layer in the specified display.
///
/// If an invalid display ID or layer ID is provided, then an empty optional is returned.
- std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const;
+ [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const;
/// Gets the vsync event for the specified display.
///
/// If an invalid display ID is provided, then nullptr is returned.
- std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
+ [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
/// Obtains a buffer queue identified by the ID.
- BufferQueue& FindBufferQueue(u32 id);
+ [[nodiscard]] BufferQueue& FindBufferQueue(u32 id);
/// Obtains a buffer queue identified by the ID.
- const BufferQueue& FindBufferQueue(u32 id) const;
+ [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const;
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished.
void Compose();
- s64 GetNextTicks() const;
+ [[nodiscard]] s64 GetNextTicks() const;
+
+ [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
+ return std::unique_lock{*guard};
+ }
private:
/// Finds the display identified by the specified ID.
- VI::Display* FindDisplay(u64 display_id);
+ [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
/// Finds the display identified by the specified ID.
- const VI::Display* FindDisplay(u64 display_id) const;
+ [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
/// Finds the layer identified by the specified ID in the desired display.
- VI::Layer* FindLayer(u64 display_id, u64 layer_id);
+ [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
/// Finds the layer identified by the specified ID in the desired display.
- const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
+ [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
+
+ static void VSyncThread(NVFlinger& nv_flinger);
+
+ void SplitVSync();
std::shared_ptr<Nvidia::Module> nvdrv;
@@ -108,7 +123,13 @@ private:
/// Event that handles screen composition.
std::shared_ptr<Core::Timing::EventType> composition_event;
+ std::shared_ptr<std::mutex> guard;
+
Core::System& system;
+
+ std::unique_ptr<std::thread> vsync_thread;
+ std::unique_ptr<Common::Event> wait_event;
+ std::atomic<bool> is_running{};
};
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp
new file mode 100644
index 000000000..aad4ca706
--- /dev/null
+++ b/src/core/hle/service/olsc/olsc.cpp
@@ -0,0 +1,69 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/service/olsc/olsc.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::OLSC {
+
+class OLSC final : public ServiceFramework<OLSC> {
+public:
+ explicit OLSC() : ServiceFramework{"olsc:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &OLSC::Initialize, "Initialize"},
+ {10, nullptr, "VerifySaveDataBackupLicenseAsync"},
+ {13, nullptr, "GetSaveDataBackupSetting"},
+ {14, &OLSC::SetSaveDataBackupSettingEnabled, "SetSaveDataBackupSettingEnabled"},
+ {15, nullptr, "SetCustomData"},
+ {16, nullptr, "DeleteSaveDataBackupSetting"},
+ {18, nullptr, "GetSaveDataBackupInfoCache"},
+ {19, nullptr, "UpdateSaveDataBackupInfoCacheAsync"},
+ {22, nullptr, "DeleteSaveDataBackupAsync"},
+ {25, nullptr, "ListDownloadableSaveDataBackupInfoAsync"},
+ {26, nullptr, "DownloadSaveDataBackupAsync"},
+ {9010, nullptr, "VerifySaveDataBackupLicenseAsyncForDebug"},
+ {9013, nullptr, "GetSaveDataBackupSettingForDebug"},
+ {9014, nullptr, "SetSaveDataBackupSettingEnabledForDebug"},
+ {9015, nullptr, "SetCustomDataForDebug"},
+ {9016, nullptr, "DeleteSaveDataBackupSettingForDebug"},
+ {9018, nullptr, "GetSaveDataBackupInfoCacheForDebug"},
+ {9019, nullptr, "UpdateSaveDataBackupInfoCacheAsyncForDebug"},
+ {9022, nullptr, "DeleteSaveDataBackupAsyncForDebug"},
+ {9025, nullptr, "ListDownloadableSaveDataBackupInfoAsyncForDebug"},
+ {9026, nullptr, "DownloadSaveDataBackupAsyncForDebug"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_OLSC, "(STUBBED) called");
+
+ initialized = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetSaveDataBackupSettingEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_OLSC, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ bool initialized{};
+};
+
+void InstallInterfaces(SM::ServiceManager& service_manager) {
+ std::make_shared<OLSC>()->InstallAsService(service_manager);
+}
+
+} // namespace Service::OLSC
diff --git a/src/core/hle/service/olsc/olsc.h b/src/core/hle/service/olsc/olsc.h
new file mode 100644
index 000000000..edee4376b
--- /dev/null
+++ b/src/core/hle/service/olsc/olsc.h
@@ -0,0 +1,16 @@
+// Copyright 2020 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::OLSC {
+
+/// Registers all SSL services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager);
+
+} // namespace Service::OLSC
diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp
index 39cf05eba..c568a0adc 100644
--- a/src/core/hle/service/pcie/pcie.cpp
+++ b/src/core/hle/service/pcie/pcie.cpp
@@ -36,6 +36,9 @@ public:
{18, nullptr, "ReleaseIrq"},
{19, nullptr, "SetIrqEnable"},
{20, nullptr, "SetAspmEnable"},
+ {21, nullptr, "SetResetUponResumeEnable"},
+ {22, nullptr, "Unknown22"},
+ {23, nullptr, "Unknown23"},
};
// clang-format on
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index c75b4ee34..caf14ed61 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -31,6 +31,8 @@ public:
{1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
{1015, nullptr, "ConfirmPlayableApplicationVideo"},
{1016, nullptr, "ConfirmShowNewsPermission"},
+ {1017, nullptr, "EndFreeCommunication"},
+ {1018, nullptr, "IsFreeCommunicationAvailable"},
{1031, nullptr, "IsRestrictionEnabled"},
{1032, nullptr, "GetSafetyLevel"},
{1033, nullptr, "SetSafetyLevel"},
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index d6891a659..8bfc0276e 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -42,6 +42,9 @@ public:
{24, nullptr, "GetModuleStateTable"},
{25, nullptr, "GetPowerDomainStateTable"},
{26, nullptr, "GetFuseInfo"},
+ {27, nullptr, "GetDramId"},
+ {28, nullptr, "IsPoweredOn"},
+ {29, nullptr, "GetVoltage"},
};
// clang-format on
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 809eca0ab..a771a51b4 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -78,13 +79,13 @@ public:
: ServiceFramework{"pm:dmnt"}, kernel(kernel) {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "GetDebugProcesses"},
- {1, nullptr, "StartDebugProcess"},
- {2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
- {3, nullptr, "EnableDebugForTitleId"},
- {4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
- {5, nullptr, "EnableDebugForApplication"},
- {6, nullptr, "DisableDebug"},
+ {0, nullptr, "GetJitDebugProcessIdList"},
+ {1, nullptr, "StartProcess"},
+ {2, &DebugMonitor::GetProcessId, "GetProcessId"},
+ {3, nullptr, "HookToCreateProcess"},
+ {4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
+ {5, nullptr, "HookToCreateApplicationProgress"},
+ {6, nullptr, "ClearHook"},
};
// clang-format on
@@ -92,7 +93,7 @@ public:
}
private:
- void GetTitlePid(Kernel::HLERequestContext& ctx) {
+ void GetProcessId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
@@ -114,7 +115,7 @@ private:
rb.Push((*process)->GetProcessID());
}
- void GetApplicationPid(Kernel::HLERequestContext& ctx) {
+ void GetApplicationProcessId(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PM, "called");
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
}
@@ -163,15 +164,15 @@ public:
: ServiceFramework{"pm:shell"}, kernel(kernel) {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "LaunchProcess"},
- {1, nullptr, "TerminateProcessByPid"},
- {2, nullptr, "TerminateProcessByTitleId"},
- {3, nullptr, "GetProcessEventWaiter"},
- {4, nullptr, "GetProcessEventType"},
+ {0, nullptr, "LaunchProgram"},
+ {1, nullptr, "TerminateProcess"},
+ {2, nullptr, "TerminateProgram"},
+ {3, nullptr, "GetProcessEventHandle"},
+ {4, nullptr, "GetProcessEventInfo"},
{5, nullptr, "NotifyBootFinished"},
- {6, &Shell::GetApplicationPid, "GetApplicationPid"},
+ {6, &Shell::GetApplicationProcessIdForShell, "GetApplicationProcessIdForShell"},
{7, nullptr, "BoostSystemMemoryResourceLimit"},
- {8, nullptr, "EnableAdditionalSystemThreads"},
+ {8, nullptr, "BoostApplicationThreadResourceLimit"},
{9, nullptr, "GetBootFinishedEventHandle"},
};
// clang-format on
@@ -180,7 +181,7 @@ public:
}
private:
- void GetApplicationPid(Kernel::HLERequestContext& ctx) {
+ void GetApplicationProcessIdForShell(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PM, "called");
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
}
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 8f1be0e48..b9ef86b72 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -4,6 +4,7 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/profile_manager.h"
@@ -21,8 +22,10 @@ public:
static const FunctionInfo functions[] = {
{10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"},
{10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
- {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
- {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
+ {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old2>, "SaveReportOld2"},
+ {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old2>, "SaveReportWithUserOld2"},
+ {10104, nullptr, "SaveReport"},
+ {10105, nullptr, "SaveReportWithUser"},
{10200, nullptr, "RequestImmediateTransmission"},
{10300, nullptr, "GetTransmissionStatus"},
{10400, nullptr, "GetSystemSessionId"},
@@ -35,9 +38,16 @@ public:
{30400, nullptr, "GetStatistics"},
{30401, nullptr, "GetThroughputHistory"},
{30500, nullptr, "GetLastUploadError"},
+ {30600, nullptr, "GetApplicationUploadSummary"},
{40100, nullptr, "IsUserAgreementCheckEnabled"},
{40101, nullptr, "SetUserAgreementCheckEnabled"},
+ {50100, nullptr, "ReadAllApplicationReportFiles"},
{90100, nullptr, "ReadAllReportFiles"},
+ {90101, nullptr, "Unknown90101"},
+ {90102, nullptr, "Unknown90102"},
+ {90200, nullptr, "GetStatistics"},
+ {90201, nullptr, "GetThroughputHistory"},
+ {90300, nullptr, "GetLastUploadError"},
};
// clang-format on
@@ -51,7 +61,7 @@ private:
const auto process_id = rp.PopRaw<u64>();
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
- if (Type == Core::Reporter::PlayReportType::New) {
+ if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
data.emplace_back(ctx.ReadBuffer(1));
}
@@ -71,8 +81,13 @@ private:
const auto user_id = rp.PopRaw<u128>();
const auto process_id = rp.PopRaw<u64>();
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
- if (Type == Core::Reporter::PlayReportType::New) {
- data.emplace_back(ctx.ReadBuffer(1));
+
+ if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
+ const auto read_buffer_count =
+ ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size();
+ if (read_buffer_count > 1) {
+ data.emplace_back(ctx.ReadBuffer(1));
+ }
}
LOG_DEBUG(
diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp
index 53ec6b031..99e1c9042 100644
--- a/src/core/hle/service/psc/psc.cpp
+++ b/src/core/hle/service/psc/psc.cpp
@@ -24,6 +24,8 @@ public:
{4, nullptr, "Cancel"},
{5, nullptr, "PrintModuleInformation"},
{6, nullptr, "GetModuleInformation"},
+ {10, nullptr, "Unknown10"},
+ {11, nullptr, "Unknown11"},
};
// clang-format on
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index c2d5fda94..6d9e6bd09 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -12,9 +12,6 @@
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"} {
@@ -38,6 +35,7 @@ public:
{15, nullptr, "GetBatteryAgePercentage"},
{16, nullptr, "GetBatteryChargeInfoEvent"},
{17, nullptr, "GetBatteryChargeInfoFields"},
+ {18, nullptr, "GetBatteryChargeCalibratedEvent"},
};
// clang-format on
@@ -48,20 +46,30 @@ public:
private:
void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_PSM, "(STUBBED) called");
+ LOG_DEBUG(Service_PSM, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(BATTERY_FULLY_CHARGED);
+ rb.Push<u32>(battery_charge_percentage);
}
void GetChargerType(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_PSM, "(STUBBED) called");
+ LOG_DEBUG(Service_PSM, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(BATTERY_CURRENTLY_CHARGING);
+ rb.PushEnum(charger_type);
}
+
+ enum class ChargerType : u32 {
+ Unplugged = 0,
+ RegularCharger = 1,
+ LowPowerCharger = 2,
+ Unknown = 3,
+ };
+
+ u32 battery_charge_percentage{100}; // 100%
+ ChargerType charger_type{ChargerType::RegularCharger};
};
void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index fa5347af9..fbfda2d5b 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -51,6 +51,7 @@
#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/olsc/olsc.h"
#include "core/hle/service/pcie/pcie.h"
#include "core/hle/service/pctl/module.h"
#include "core/hle/service/pcv/pcv.h"
@@ -89,8 +90,6 @@ namespace Service {
return function_string;
}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
InvokerFn* handler_invoker)
: service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
@@ -105,10 +104,9 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
port_installed = true;
}
-void ServiceFrameworkBase::InstallAsNamedPort() {
+void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
ASSERT(!port_installed);
- auto& kernel = Core::System::GetInstance().Kernel();
auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
server_port->SetHleHandler(shared_from_this());
@@ -116,10 +114,9 @@ void ServiceFrameworkBase::InstallAsNamedPort() {
port_installed = true;
}
-std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
+std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
ASSERT(!port_installed);
- auto& kernel = Core::System::GetInstance().Kernel();
auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
auto port = MakeResult(std::move(server_port)).Unwrap();
@@ -191,9 +188,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
return RESULT_SUCCESS;
}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Module interface
-
/// Initialize ServiceManager
void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
@@ -238,6 +232,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
NPNS::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm, system);
Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
+ OLSC::InstallInterfaces(*sm);
PCIe::InstallInterfaces(*sm);
PCTL::InstallInterfaces(*sm);
PCV::InstallInterfaces(*sm);
@@ -246,7 +241,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
PSC::InstallInterfaces(*sm);
PSM::InstallInterfaces(*sm);
Set::InstallInterfaces(*sm);
- Sockets::InstallInterfaces(*sm);
+ Sockets::InstallInterfaces(*sm, system);
SPL::InstallInterfaces(*sm);
SSL::InstallInterfaces(*sm);
Time::InstallInterfaces(system);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 022d885b6..a01ef3353 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -63,9 +63,9 @@ public:
/// Creates a port pair and registers this service with the given ServiceManager.
void InstallAsService(SM::ServiceManager& service_manager);
/// Creates a port pair and registers it on the kernel's global port registry.
- void InstallAsNamedPort();
+ void InstallAsNamedPort(Kernel::KernelCore& kernel);
/// Creates and returns an unregistered port for the service.
- std::shared_ptr<Kernel::ClientPort> CreatePort();
+ std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel);
void InvokeRequest(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 9e12c76fc..ffbf90b00 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <array>
#include <chrono>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
@@ -31,6 +32,44 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::ZH_HANT,
}};
+enum class KeyboardLayout : u64 {
+ Japanese = 0,
+ EnglishUs = 1,
+ EnglishUsInternational = 2,
+ EnglishUk = 3,
+ French = 4,
+ FrenchCa = 5,
+ Spanish = 6,
+ SpanishLatin = 7,
+ German = 8,
+ Italian = 9,
+ Portuguese = 10,
+ Russian = 11,
+ Korean = 12,
+ ChineseSimplified = 13,
+ ChineseTraditional = 14,
+};
+
+constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
+ {LanguageCode::JA, KeyboardLayout::Japanese},
+ {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
+ {LanguageCode::FR, KeyboardLayout::French},
+ {LanguageCode::DE, KeyboardLayout::German},
+ {LanguageCode::IT, KeyboardLayout::Italian},
+ {LanguageCode::ES, KeyboardLayout::Spanish},
+ {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified},
+ {LanguageCode::KO, KeyboardLayout::Korean},
+ {LanguageCode::NL, KeyboardLayout::EnglishUsInternational},
+ {LanguageCode::PT, KeyboardLayout::Portuguese},
+ {LanguageCode::RU, KeyboardLayout::Russian},
+ {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional},
+ {LanguageCode::EN_GB, KeyboardLayout::EnglishUk},
+ {LanguageCode::FR_CA, KeyboardLayout::FrenchCa},
+ {LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
+ {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
+ {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
+}};
+
constexpr std::size_t pre4_0_0_max_entries = 15;
constexpr std::size_t post4_0_0_max_entries = 17;
@@ -50,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
ctx.WriteBuffer(available_language_codes.data(), copy_size);
PushResponseLanguageCode(ctx, copy_amount);
}
+
+void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
+ const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
+ const auto key_code =
+ std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
+ [=](const auto& element) { return element.first == language_code; });
+ KeyboardLayout layout = KeyboardLayout::EnglishUs;
+ if (key_code == language_to_layout.cend()) {
+ LOG_ERROR(Service_SET,
+ "Could not find keyboard layout for language index {}, defaulting to English us",
+ Settings::values.language_index.GetValue());
+ } else {
+ layout = key_code->second;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ ctx.WriteBuffer(layout);
+}
} // Anonymous namespace
LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
@@ -67,6 +125,7 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
const auto index = rp.Pop<u32>();
if (index >= available_language_codes.size()) {
+ LOG_ERROR(Service_SET, "Invalid language code index! index={}", index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_LANGUAGE);
return;
@@ -104,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
}
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
+ LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.PushEnum(available_language_codes[Settings::values.language_index]);
+ rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
}
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
@@ -116,7 +175,17 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(Settings::values.region_index);
+ rb.Push(Settings::values.region_index.GetValue());
+}
+
+void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
+ GetKeyCodeMapImpl(ctx);
+}
+
+void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
+ GetKeyCodeMapImpl(ctx);
}
SET::SET() : ServiceFramework("set") {
@@ -129,10 +198,11 @@ SET::SET() : ServiceFramework("set") {
{4, &SET::GetRegionCode, "GetRegionCode"},
{5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
{6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
- {7, nullptr, "GetKeyCodeMap"},
+ {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"},
{8, &SET::GetQuestFlag, "GetQuestFlag"},
- {9, nullptr, "GetKeyCodeMap2"},
+ {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
{10, nullptr, "GetFirmwareVersionForDebug"},
+ {11, nullptr, "GetDeviceNickName"},
};
// clang-format on
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 6084b345d..8ac9c169d 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -44,6 +44,8 @@ private:
void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
void GetQuestFlag(Kernel::HLERequestContext& ctx);
void GetRegionCode(Kernel::HLERequestContext& ctx);
+ void GetKeyCodeMap(Kernel::HLERequestContext& ctx);
+ void GetKeyCodeMap2(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 1398a4a48..3fbfecc9e 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -50,6 +50,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
{39, nullptr, "GetConsoleSixAxisSensorModuleType"},
{40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"},
{41, nullptr, "GetBatteryVersion"},
+ {42, nullptr, "GetDeviceId"},
+ {43, nullptr, "GetConsoleSixAxisSensorMountType"},
};
// clang-format on
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index b7c9ea74b..080b5743e 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -288,6 +288,20 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
{186, nullptr, "GetMemoryUsageRateFlag"},
{187, nullptr, "GetTouchScreenMode"},
{188, nullptr, "SetTouchScreenMode"},
+ {189, nullptr, "GetButtonConfigSettingsFull"},
+ {190, nullptr, "SetButtonConfigSettingsFull"},
+ {191, nullptr, "GetButtonConfigSettingsEmbedded"},
+ {192, nullptr, "SetButtonConfigSettingsEmbedded"},
+ {193, nullptr, "GetButtonConfigSettingsLeft"},
+ {194, nullptr, "SetButtonConfigSettingsLeft"},
+ {195, nullptr, "GetButtonConfigSettingsRight"},
+ {196, nullptr, "SetButtonConfigSettingsRight"},
+ {197, nullptr, "GetButtonConfigRegisteredSettingsEmbedded"},
+ {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"},
+ {199, nullptr, "GetButtonConfigRegisteredSettings"},
+ {200, nullptr, "SetButtonConfigRegisteredSettings"},
+ {201, nullptr, "GetFieldTestingFlag"},
+ {202, nullptr, "SetFieldTestingFlag"},
};
// clang-format on
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index 9cca84b31..972aaa6d9 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -12,7 +12,7 @@
namespace Service::SM {
-void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
+void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
ctx.Session()->ConvertToDomain();
@@ -22,7 +22,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(1); // Converted sessions start with 1 request handler
}
-void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
+void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
// and that we probably want to actually make an entirely new Session, but we still need to
// verify this on hardware.
@@ -33,10 +33,10 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
}
-void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession");
+void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) called, using CloneCurrentObject");
- DuplicateSession(ctx);
+ CloneCurrentObject(ctx);
}
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
@@ -47,13 +47,14 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
rb.Push<u16>(0x1000);
}
+// https://switchbrew.org/wiki/IPC_Marshalling
Controller::Controller() : ServiceFramework("IpcController") {
static const FunctionInfo functions[] = {
- {0x00000000, &Controller::ConvertSessionToDomain, "ConvertSessionToDomain"},
- {0x00000001, nullptr, "ConvertDomainToSession"},
- {0x00000002, &Controller::DuplicateSession, "DuplicateSession"},
- {0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
- {0x00000004, &Controller::DuplicateSessionEx, "DuplicateSessionEx"},
+ {0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"},
+ {1, nullptr, "CopyFromCurrentDomain"},
+ {2, &Controller::CloneCurrentObject, "CloneCurrentObject"},
+ {3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
+ {4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/controller.h
index dc66c9e37..180c6da50 100644
--- a/src/core/hle/service/sm/controller.h
+++ b/src/core/hle/service/sm/controller.h
@@ -14,9 +14,9 @@ public:
~Controller() override;
private:
- void ConvertSessionToDomain(Kernel::HLERequestContext& ctx);
- void DuplicateSession(Kernel::HLERequestContext& ctx);
- void DuplicateSessionEx(Kernel::HLERequestContext& ctx);
+ void ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx);
+ void CloneCurrentObject(Kernel::HLERequestContext& ctx);
+ void CloneCurrentObjectEx(Kernel::HLERequestContext& ctx);
void QueryPointerBufferSize(Kernel::HLERequestContext& ctx);
};
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 88909504d..9c1da361b 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -19,7 +19,7 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
-ServiceManager::ServiceManager() = default;
+ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
ServiceManager::~ServiceManager() = default;
void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
@@ -27,10 +27,12 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
}
static ResultCode ValidateServiceName(const std::string& name) {
- if (name.size() <= 0 || name.size() > 8) {
+ if (name.empty() || name.size() > 8) {
+ LOG_ERROR(Service_SM, "Invalid service name! service={}", name);
return ERR_INVALID_NAME;
}
- if (name.find('\0') != std::string::npos) {
+ if (name.rfind('\0') != std::string::npos) {
+ LOG_ERROR(Service_SM, "A non null terminated service was passed");
return ERR_INVALID_NAME;
}
return RESULT_SUCCESS;
@@ -41,20 +43,21 @@ void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self,
ASSERT(self->sm_interface.expired());
auto sm = std::make_shared<SM>(self, kernel);
- sm->InstallAsNamedPort();
+ sm->InstallAsNamedPort(kernel);
self->sm_interface = sm;
self->controller_interface = std::make_unique<Controller>();
}
-ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(
- std::string name, unsigned int max_sessions) {
+ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(std::string name,
+ u32 max_sessions) {
CASCADE_CODE(ValidateServiceName(name));
- if (registered_services.find(name) != registered_services.end())
+ if (registered_services.find(name) != registered_services.end()) {
+ LOG_ERROR(Service_SM, "Service is already registered! service={}", name);
return ERR_ALREADY_REGISTERED;
+ }
- auto& kernel = Core::System::GetInstance().Kernel();
auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name);
@@ -66,9 +69,10 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) {
CASCADE_CODE(ValidateServiceName(name));
const auto iter = registered_services.find(name);
- if (iter == registered_services.end())
+ if (iter == registered_services.end()) {
+ LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
return ERR_SERVICE_NOT_REGISTERED;
-
+ }
registered_services.erase(iter);
return RESULT_SUCCESS;
}
@@ -79,6 +83,7 @@ ResultVal<std::shared_ptr<Kernel::ClientPort>> ServiceManager::GetServicePort(
CASCADE_CODE(ValidateServiceName(name));
auto it = registered_services.find(name);
if (it == registered_services.end()) {
+ LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
return ERR_SERVICE_NOT_REGISTERED;
}
@@ -136,7 +141,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
}
// Wake the threads waiting on the ServerPort
- server_port->WakeupAllWaitingThreads();
+ server_port->Signal();
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index b06d2f103..6790c86f0 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -9,6 +9,7 @@
#include <type_traits>
#include <unordered_map>
+#include "common/concepts.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
@@ -47,19 +48,17 @@ class ServiceManager {
public:
static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel);
- ServiceManager();
+ explicit ServiceManager(Kernel::KernelCore& kernel_);
~ServiceManager();
ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name,
- unsigned int max_sessions);
+ u32 max_sessions);
ResultCode UnregisterService(const std::string& name);
ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
- template <typename T>
+ template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& service_name) const {
- static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
- "Not a base of ServiceFrameworkBase");
auto service = registered_services.find(service_name);
if (service == registered_services.end()) {
LOG_DEBUG(Service, "Can't find service: {}", service_name);
@@ -80,6 +79,9 @@ private:
/// Map of registered services, retrieved using GetServicePort or ConnectToService.
std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services;
+
+ /// Kernel context
+ Kernel::KernelCore& kernel;
};
} // namespace Service::SM
diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h
new file mode 100644
index 000000000..2d53e52b6
--- /dev/null
+++ b/src/core/hle/service/sockets/blocking_worker.h
@@ -0,0 +1,161 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <variant>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include "common/assert.h"
+#include "common/microprofile.h"
+#include "common/thread.h"
+#include "core/core.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
+
+namespace Service::Sockets {
+
+/**
+ * Worker abstraction to execute blocking calls on host without blocking the guest thread
+ *
+ * @tparam Service Service where the work is executed
+ * @tparam Types Types of work to execute
+ */
+template <class Service, class... Types>
+class BlockingWorker {
+ using This = BlockingWorker<Service, Types...>;
+ using WorkVariant = std::variant<std::monostate, Types...>;
+
+public:
+ /// Create a new worker
+ static std::unique_ptr<This> Create(Core::System& system, Service* service,
+ std::string_view name) {
+ return std::unique_ptr<This>(new This(system, service, name));
+ }
+
+ ~BlockingWorker() {
+ while (!is_available.load(std::memory_order_relaxed)) {
+ // Busy wait until work is finished
+ std::this_thread::yield();
+ }
+ // Monostate means to exit the thread
+ work = std::monostate{};
+ work_event.Set();
+ thread.join();
+ }
+
+ /**
+ * Try to capture the worker to send work after a success
+ * @returns True when the worker has been successfully captured
+ */
+ bool TryCapture() {
+ bool expected = true;
+ return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
+ std::memory_order_relaxed);
+ }
+
+ /**
+ * Send work to this worker abstraction
+ * @see TryCapture must be called before attempting to call this function
+ */
+ template <class Work>
+ void SendWork(Work new_work) {
+ ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
+ work = std::move(new_work);
+ work_event.Set();
+ }
+
+ /// Generate a callback for @see SleepClientThread
+ template <class Work>
+ auto Callback() {
+ return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
+ Kernel::ThreadWakeupReason reason) {
+ ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
+ std::get<Work>(work).Response(ctx);
+ is_available.store(true);
+ };
+ }
+
+ /// Get kernel event that will be signalled by the worker when the host operation finishes
+ std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
+ return kernel_event;
+ }
+
+private:
+ explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
+ auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
+ kernel_event = std::move(pair.writable);
+ thread = std::thread([this, &system, service, name] { Run(system, service, name); });
+ }
+
+ void Run(Core::System& system, Service* service, std::string_view name) {
+ system.RegisterHostThread();
+
+ const std::string thread_name = fmt::format("yuzu:{}", name);
+ MicroProfileOnThreadCreate(thread_name.c_str());
+ Common::SetCurrentThreadName(thread_name.c_str());
+
+ bool keep_running = true;
+ while (keep_running) {
+ work_event.Wait();
+
+ const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
+ if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
+ keep_running = false;
+ } else {
+ w.Execute(service);
+ }
+ };
+ std::visit(visit_fn, work);
+
+ kernel_event->Signal();
+ }
+ }
+
+ std::thread thread;
+ WorkVariant work;
+ Common::Event work_event;
+ std::shared_ptr<Kernel::WritableEvent> kernel_event;
+ std::atomic_bool is_available{true};
+};
+
+template <class Service, class... Types>
+class BlockingWorkerPool {
+ using Worker = BlockingWorker<Service, Types...>;
+
+public:
+ explicit BlockingWorkerPool(Core::System& system_, Service* service_)
+ : system{system_}, service{service_} {}
+
+ /// Returns a captured worker thread, creating new ones if necessary
+ Worker* CaptureWorker() {
+ for (auto& worker : workers) {
+ if (worker->TryCapture()) {
+ return worker.get();
+ }
+ }
+ auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
+ [[maybe_unused]] const bool success = new_worker->TryCapture();
+ ASSERT(success);
+
+ return workers.emplace_back(std::move(new_worker)).get();
+ }
+
+private:
+ Core::System& system;
+ Service* const service;
+
+ std::vector<std::unique_ptr<Worker>> workers;
+};
+
+} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index f67fab2f9..a74be9370 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -2,18 +2,138 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include "common/microprofile.h"
+#include "common/thread.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/thread.h"
#include "core/hle/service/sockets/bsd.h"
+#include "core/hle/service/sockets/sockets_translate.h"
+#include "core/network/network.h"
+#include "core/network/sockets.h"
namespace Service::Sockets {
+namespace {
+
+bool IsConnectionBased(Type type) {
+ switch (type) {
+ case Type::STREAM:
+ return true;
+ case Type::DGRAM:
+ return false;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
+ return false;
+ }
+}
+
+} // Anonymous namespace
+
+void BSD::PollWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout);
+}
+
+void BSD::PollWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::AcceptWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer);
+}
+
+void BSD::AcceptWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(write_buffer.size()));
+}
+
+void BSD::ConnectWork::Execute(BSD* bsd) {
+ bsd_errno = bsd->ConnectImpl(fd, addr);
+}
+
+void BSD::ConnectWork::Response(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::RecvWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message);
+}
+
+void BSD::RecvWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(message);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::RecvFromWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr);
+}
+
+void BSD::RecvFromWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(message, 0);
+ if (!addr.empty()) {
+ ctx.WriteBuffer(addr, 1);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(addr.size()));
+}
+
+void BSD::SendWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message);
+}
+
+void BSD::SendWork::Response(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::SendToWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr);
+}
+
+void BSD::SendToWork::Response(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(0); // bsd errno
}
void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
@@ -26,20 +146,19 @@ void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
void BSD::Socket(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ const u32 domain = rp.Pop<u32>();
+ const u32 type = rp.Pop<u32>();
+ const u32 protocol = rp.Pop<u32>();
- u32 domain = rp.Pop<u32>();
- u32 type = rp.Pop<u32>();
- u32 protocol = rp.Pop<u32>();
-
- LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol);
+ LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
- u32 fd = next_fd++;
+ const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type),
+ static_cast<Protocol>(protocol));
IPC::ResponseBuilder rb{ctx, 4};
-
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(fd);
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(fd);
+ rb.PushEnum(bsd_errno);
}
void BSD::Select(Kernel::HLERequestContext& ctx) {
@@ -52,67 +171,664 @@ void BSD::Select(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno
}
+void BSD::Poll(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 nfds = rp.Pop<s32>();
+ const s32 timeout = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
+
+ ExecuteWork(ctx, "BSD:Poll", timeout != 0,
+ PollWork{
+ .nfds = nfds,
+ .timeout = timeout,
+ .read_buffer = ctx.ReadBuffer(),
+ .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
+ });
+}
+
+void BSD::Accept(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+
+ ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd),
+ AcceptWork{
+ .fd = fd,
+ .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
+ });
+}
+
void BSD::Bind(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
- IPC::ResponseBuilder rb{ctx, 4};
+ LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
}
void BSD::Connect(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
- IPC::ResponseBuilder rb{ctx, 4};
+ LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
+
+ ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd),
+ ConnectWork{
+ .fd = fd,
+ .addr = ctx.ReadBuffer(),
+ });
+}
+
+void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+ std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
+ const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
+
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(write_buffer.size()));
+}
+
+void BSD::GetSockName(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+
+ std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
+ const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
+
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
void BSD::Listen(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+ const s32 backlog = rp.Pop<s32>();
- IPC::ResponseBuilder rb{ctx, 4};
+ LOG_DEBUG(Service, "called. fd={} backlog={}", fd, backlog);
+
+ BuildErrnoResponse(ctx, ListenImpl(fd, backlog));
+}
+
+void BSD::Fcntl(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+ const s32 cmd = rp.Pop<s32>();
+ const s32 arg = rp.Pop<s32>();
+ LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
+
+ const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
+
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
}
void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
- IPC::ResponseBuilder rb{ctx, 4};
+ const s32 fd = rp.Pop<s32>();
+ const u32 level = rp.Pop<u32>();
+ const OptName optname = static_cast<OptName>(rp.Pop<u32>());
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ const std::vector<u8> buffer = ctx.ReadBuffer();
+ const u8* optval = buffer.empty() ? nullptr : buffer.data();
+ size_t optlen = buffer.size();
+
+ std::array<u64, 2> values;
+ if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) {
+ std::memcpy(values.data(), buffer.data(), sizeof(values));
+ optlen = sizeof(values);
+ optval = reinterpret_cast<const u8*>(values.data());
+ }
+
+ LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
+ static_cast<u32>(optname), optlen);
+
+ BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval));
+}
+
+void BSD::Shutdown(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const s32 how = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={} how={}", fd, how);
+
+ BuildErrnoResponse(ctx, ShutdownImpl(fd, how));
+}
+
+void BSD::Recv(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
+
+ ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd),
+ RecvWork{
+ .fd = fd,
+ .flags = flags,
+ .message = std::vector<u8>(ctx.GetWriteBufferSize()),
+ });
+}
+
+void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
+ ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
+
+ ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd),
+ RecvFromWork{
+ .fd = fd,
+ .flags = flags,
+ .message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
+ .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
+ });
+}
+
+void BSD::Send(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
+
+ ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd),
+ SendWork{
+ .fd = fd,
+ .flags = flags,
+ .message = ctx.ReadBuffer(),
+ });
}
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
+ ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
+
+ ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd),
+ SendToWork{
+ .fd = fd,
+ .flags = flags,
+ .message = ctx.ReadBuffer(0),
+ .addr = ctx.ReadBuffer(1),
+ });
+}
- IPC::ResponseBuilder rb{ctx, 4};
+void BSD::Write(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
+
+ ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd),
+ SendWork{
+ .fd = fd,
+ .flags = 0,
+ .message = ctx.ReadBuffer(),
+ });
}
void BSD::Close(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+
+ BuildErrnoResponse(ctx, CloseImpl(fd));
+}
+
+template <typename Work>
+void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
+ bool is_blocking, Work work) {
+ if (!is_blocking) {
+ work.Execute(this);
+ work.Response(ctx);
+ return;
+ }
+
+ // Signal a dummy response to make IPC validation happy
+ // This will be overwritten by the SleepClientThread callback
+ work.Response(ctx);
+
+ auto worker = worker_pool.CaptureWorker();
+
+ ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
+ worker->Callback<Work>(), worker->KernelEvent());
+
+ worker->SendWork(std::move(work));
+}
+
+std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
+ if (type == Type::SEQPACKET) {
+ UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management");
+ } else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) {
+ UNIMPLEMENTED_MSG("SOCK_RAW errno management");
+ }
+
+ [[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
+ UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
+ type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
+
+ const s32 fd = FindFreeFileDescriptorHandle();
+ if (fd < 0) {
+ LOG_ERROR(Service, "No more file descriptors available");
+ return {-1, Errno::MFILE};
+ }
+
+ FileDescriptor& descriptor = file_descriptors[fd].emplace();
+ // ENONMEM might be thrown here
+
+ LOG_INFO(Service, "New socket fd={}", fd);
+
+ descriptor.socket = std::make_unique<Network::Socket>();
+ descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
+ descriptor.is_connection_based = IsConnectionBased(type);
+
+ return {fd, Errno::SUCCESS};
+}
+std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
+ s32 nfds, s32 timeout) {
+ if (write_buffer.size() < nfds * sizeof(PollFD)) {
+ return {-1, Errno::INVAL};
+ }
+
+ if (nfds == 0) {
+ // When no entries are provided, -1 is returned with errno zero
+ return {-1, Errno::SUCCESS};
+ }
+
+ const size_t length = std::min(read_buffer.size(), write_buffer.size());
+ std::vector<PollFD> fds(nfds);
+ std::memcpy(fds.data(), read_buffer.data(), length);
+
+ if (timeout >= 0) {
+ const s64 seconds = timeout / 1000;
+ const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
+
+ if (seconds < 0) {
+ return {-1, Errno::INVAL};
+ }
+ if (nanoseconds > 999'999'999) {
+ return {-1, Errno::INVAL};
+ }
+ } else if (timeout != -1) {
+ return {-1, Errno::INVAL};
+ }
+
+ for (PollFD& pollfd : fds) {
+ ASSERT(pollfd.revents == 0);
+
+ if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
+ LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
+ pollfd.revents = 0;
+ return {0, Errno::SUCCESS};
+ }
+
+ const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
+ if (!descriptor) {
+ LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd);
+ pollfd.revents = POLL_NVAL;
+ return {0, Errno::SUCCESS};
+ }
+ }
+
+ std::vector<Network::PollFD> host_pollfds(fds.size());
+ std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) {
+ Network::PollFD result;
+ result.socket = file_descriptors[pollfd.fd]->socket.get();
+ result.events = TranslatePollEventsToHost(pollfd.events);
+ result.revents = 0;
+ return result;
+ });
+
+ const auto result = Network::Poll(host_pollfds, timeout);
+
+ const size_t num = host_pollfds.size();
+ for (size_t i = 0; i < num; ++i) {
+ fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents);
+ }
+ std::memcpy(write_buffer.data(), fds.data(), length);
+
+ return Translate(result);
+}
+
+std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ const s32 new_fd = FindFreeFileDescriptorHandle();
+ if (new_fd < 0) {
+ LOG_ERROR(Service, "No more file descriptors available");
+ return {-1, Errno::MFILE};
+ }
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+ auto [result, bsd_errno] = descriptor.socket->Accept();
+ if (bsd_errno != Network::Errno::SUCCESS) {
+ return {-1, Translate(bsd_errno)};
+ }
+
+ FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace();
+ new_descriptor.socket = std::move(result.socket);
+ new_descriptor.is_connection_based = descriptor.is_connection_based;
+
+ ASSERT(write_buffer.size() == sizeof(SockAddrIn));
+ const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
+ std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in));
+
+ return {new_fd, Errno::SUCCESS};
+}
+
+Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+ ASSERT(addr.size() == sizeof(SockAddrIn));
+ SockAddrIn addr_in;
+ std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
+
+ return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
+}
+
+Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
+ SockAddrIn addr_in;
+ std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
+
+ return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
+}
+
+Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
+ if (bsd_errno != Network::Errno::SUCCESS) {
+ return Translate(bsd_errno);
+ }
+ const SockAddrIn guest_addrin = Translate(addr_in);
+
+ ASSERT(write_buffer.size() == sizeof(guest_addrin));
+ std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
+ return Translate(bsd_errno);
+}
+
+Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
+ if (bsd_errno != Network::Errno::SUCCESS) {
+ return Translate(bsd_errno);
+ }
+ const SockAddrIn guest_addrin = Translate(addr_in);
+
+ ASSERT(write_buffer.size() == sizeof(guest_addrin));
+ std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
+ return Translate(bsd_errno);
+}
+
+Errno BSD::ListenImpl(s32 fd, s32 backlog) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+ return Translate(file_descriptors[fd]->socket->Listen(backlog));
+}
+
+std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+
+ switch (cmd) {
+ case FcntlCmd::GETFL:
+ ASSERT(arg == 0);
+ return {descriptor.flags, Errno::SUCCESS};
+ case FcntlCmd::SETFL: {
+ const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
+ const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
+ if (bsd_errno != Errno::SUCCESS) {
+ return {-1, bsd_errno};
+ }
+ descriptor.flags = arg;
+ return {0, Errno::SUCCESS};
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented cmd={}", static_cast<int>(cmd));
+ return {-1, Errno::SUCCESS};
+ }
+}
+
+Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) {
+ UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET
+
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ Network::Socket* const socket = file_descriptors[fd]->socket.get();
+
+ if (optname == OptName::LINGER) {
+ ASSERT(optlen == sizeof(Linger));
+ Linger linger;
+ std::memcpy(&linger, optval, sizeof(linger));
+ ASSERT(linger.onoff == 0 || linger.onoff == 1);
+
+ return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
+ }
+
+ ASSERT(optlen == sizeof(u32));
+ u32 value;
+ std::memcpy(&value, optval, sizeof(value));
+
+ switch (optname) {
+ case OptName::REUSEADDR:
+ ASSERT(value == 0 || value == 1);
+ return Translate(socket->SetReuseAddr(value != 0));
+ case OptName::BROADCAST:
+ ASSERT(value == 0 || value == 1);
+ return Translate(socket->SetBroadcast(value != 0));
+ case OptName::SNDBUF:
+ return Translate(socket->SetSndBuf(value));
+ case OptName::RCVBUF:
+ return Translate(socket->SetRcvBuf(value));
+ case OptName::SNDTIMEO:
+ return Translate(socket->SetSndTimeo(value));
+ case OptName::RCVTIMEO:
+ return Translate(socket->SetRcvTimeo(value));
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented optname={}", static_cast<int>(optname));
+ return Errno::SUCCESS;
+ }
+}
+
+Errno BSD::ShutdownImpl(s32 fd, s32 how) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+ const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
+ return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
+}
+
+std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+ return Translate(file_descriptors[fd]->socket->Recv(flags, message));
+}
+
+std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
+ std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+
+ Network::SockAddrIn addr_in{};
+ Network::SockAddrIn* p_addr_in = nullptr;
+ if (descriptor.is_connection_based) {
+ // Connection based file descriptors (e.g. TCP) zero addr
+ addr.clear();
+ } else {
+ p_addr_in = &addr_in;
+ }
+
+ // Apply flags
+ if ((flags & FLAG_MSG_DONTWAIT) != 0) {
+ flags &= ~FLAG_MSG_DONTWAIT;
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(true);
+ }
+ }
+
+ const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
+
+ // Restore original state
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(false);
+ }
+
+ if (p_addr_in) {
+ if (ret < 0) {
+ addr.clear();
+ } else {
+ ASSERT(addr.size() == sizeof(SockAddrIn));
+ const SockAddrIn result = Translate(addr_in);
+ std::memcpy(addr.data(), &result, sizeof(result));
+ }
+ }
+
+ return {ret, bsd_errno};
+}
+
+std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+ return Translate(file_descriptors[fd]->socket->Send(message, flags));
+}
+
+std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
+ const std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ Network::SockAddrIn addr_in;
+ Network::SockAddrIn* p_addr_in = nullptr;
+ if (!addr.empty()) {
+ ASSERT(addr.size() == sizeof(SockAddrIn));
+ SockAddrIn guest_addr_in;
+ std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
+ addr_in = Translate(guest_addr_in);
+ p_addr_in = &addr_in;
+ }
+
+ return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
+}
+
+Errno BSD::CloseImpl(s32 fd) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
+ if (bsd_errno != Errno::SUCCESS) {
+ return bsd_errno;
+ }
+
+ LOG_INFO(Service, "Close socket fd={}", fd);
+
+ file_descriptors[fd].reset();
+ return bsd_errno;
+}
+
+s32 BSD::FindFreeFileDescriptorHandle() noexcept {
+ for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
+ if (!file_descriptors[fd]) {
+ return fd;
+ }
+ }
+ return -1;
+}
+
+bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
+ if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
+ LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
+ return false;
+ }
+ if (!file_descriptors[fd]) {
+ LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
+ return false;
+ }
+ return true;
+}
+
+bool BSD::IsBlockingSocket(s32 fd) const noexcept {
+ // Inform invalid sockets as non-blocking
+ // This way we avoid using a worker thread as it will fail without blocking host
+ if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
+ return false;
+ }
+ if (!file_descriptors[fd]) {
+ return false;
+ }
+ return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
+}
+
+void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
+ rb.PushEnum(bsd_errno);
}
-BSD::BSD(const char* name) : ServiceFramework(name) {
+BSD::BSD(Core::System& system, const char* name)
+ : ServiceFramework(name), worker_pool{system, this} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@@ -121,25 +837,25 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{3, nullptr, "SocketExempt"},
{4, nullptr, "Open"},
{5, &BSD::Select, "Select"},
- {6, nullptr, "Poll"},
+ {6, &BSD::Poll, "Poll"},
{7, nullptr, "Sysctl"},
- {8, nullptr, "Recv"},
- {9, nullptr, "RecvFrom"},
- {10, nullptr, "Send"},
+ {8, &BSD::Recv, "Recv"},
+ {9, &BSD::RecvFrom, "RecvFrom"},
+ {10, &BSD::Send, "Send"},
{11, &BSD::SendTo, "SendTo"},
- {12, nullptr, "Accept"},
+ {12, &BSD::Accept, "Accept"},
{13, &BSD::Bind, "Bind"},
{14, &BSD::Connect, "Connect"},
- {15, nullptr, "GetPeerName"},
- {16, nullptr, "GetSockName"},
+ {15, &BSD::GetPeerName, "GetPeerName"},
+ {16, &BSD::GetSockName, "GetSockName"},
{17, nullptr, "GetSockOpt"},
{18, &BSD::Listen, "Listen"},
{19, nullptr, "Ioctl"},
- {20, nullptr, "Fcntl"},
+ {20, &BSD::Fcntl, "Fcntl"},
{21, &BSD::SetSockOpt, "SetSockOpt"},
- {22, nullptr, "Shutdown"},
+ {22, &BSD::Shutdown, "Shutdown"},
{23, nullptr, "ShutdownAllSockets"},
- {24, nullptr, "Write"},
+ {24, &BSD::Write, "Write"},
{25, nullptr, "Read"},
{26, &BSD::Close, "Close"},
{27, nullptr, "DuplicateSocket"},
@@ -148,6 +864,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{30, nullptr, "SendMMsg"},
{31, nullptr, "EventFd"},
{32, nullptr, "RegisterResourceStatisticsName"},
+ {33, nullptr, "Initialize2"},
};
// clang-format on
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 3098e3baf..357531951 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -4,30 +4,174 @@
#pragma once
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include "common/common_types.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/sockets/blocking_worker.h"
+#include "core/hle/service/sockets/sockets.h"
+
+namespace Core {
+class System;
+}
+
+namespace Network {
+class Socket;
+}
namespace Service::Sockets {
class BSD final : public ServiceFramework<BSD> {
public:
- explicit BSD(const char* name);
+ explicit BSD(Core::System& system, const char* name);
~BSD() override;
private:
+ /// Maximum number of file descriptors
+ static constexpr size_t MAX_FD = 128;
+
+ struct FileDescriptor {
+ std::unique_ptr<Network::Socket> socket;
+ s32 flags = 0;
+ bool is_connection_based = false;
+ };
+
+ struct PollWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 nfds;
+ s32 timeout;
+ std::vector<u8> read_buffer;
+ std::vector<u8> write_buffer;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct AcceptWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ std::vector<u8> write_buffer;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct ConnectWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ std::vector<u8> addr;
+ Errno bsd_errno{};
+ };
+
+ struct RecvWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct RecvFromWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ std::vector<u8> addr;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct SendWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct SendToWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ std::vector<u8> addr;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
void RegisterClient(Kernel::HLERequestContext& ctx);
void StartMonitoring(Kernel::HLERequestContext& ctx);
void Socket(Kernel::HLERequestContext& ctx);
void Select(Kernel::HLERequestContext& ctx);
+ void Poll(Kernel::HLERequestContext& ctx);
+ void Accept(Kernel::HLERequestContext& ctx);
void Bind(Kernel::HLERequestContext& ctx);
void Connect(Kernel::HLERequestContext& ctx);
+ void GetPeerName(Kernel::HLERequestContext& ctx);
+ void GetSockName(Kernel::HLERequestContext& ctx);
void Listen(Kernel::HLERequestContext& ctx);
+ void Fcntl(Kernel::HLERequestContext& ctx);
void SetSockOpt(Kernel::HLERequestContext& ctx);
+ void Shutdown(Kernel::HLERequestContext& ctx);
+ void Recv(Kernel::HLERequestContext& ctx);
+ void RecvFrom(Kernel::HLERequestContext& ctx);
+ void Send(Kernel::HLERequestContext& ctx);
void SendTo(Kernel::HLERequestContext& ctx);
+ void Write(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
- /// Id to use for the next open file descriptor.
- u32 next_fd = 1;
+ template <typename Work>
+ void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
+ bool is_blocking, Work work);
+
+ std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
+ std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
+ s32 nfds, s32 timeout);
+ std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
+ Errno BindImpl(s32 fd, const std::vector<u8>& addr);
+ Errno ConnectImpl(s32 fd, const std::vector<u8>& addr);
+ Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
+ Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
+ Errno ListenImpl(s32 fd, s32 backlog);
+ std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
+ Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval);
+ Errno ShutdownImpl(s32 fd, s32 how);
+ std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
+ std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
+ std::vector<u8>& addr);
+ std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message);
+ std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
+ const std::vector<u8>& addr);
+ Errno CloseImpl(s32 fd);
+
+ s32 FindFreeFileDescriptorHandle() noexcept;
+ bool IsFileDescriptorValid(s32 fd) const noexcept;
+ bool IsBlockingSocket(s32 fd) const noexcept;
+
+ void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
+
+ std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
+
+ BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
+ SendToWork>
+ worker_pool;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp
index dc70fd6fe..40d781124 100644
--- a/src/core/hle/service/sockets/nsd.cpp
+++ b/src/core/hle/service/sockets/nsd.cpp
@@ -14,6 +14,7 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
{12, nullptr, "GetDeviceId"},
{13, nullptr, "DeleteSettings"},
{14, nullptr, "ImportSettings"},
+ {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
{20, nullptr, "Resolve"},
{21, nullptr, "ResolveEx"},
{30, nullptr, "GetNasServiceSetting"},
@@ -28,6 +29,11 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
{60, nullptr, "ReadSaveDataFromFsForTest"},
{61, nullptr, "WriteSaveDataToFsForTest"},
{62, nullptr, "DeleteSaveDataOfFsForTest"},
+ {63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
+ {64, nullptr, "SetWithoutDomainExchangeFqdns"},
+ {100, nullptr, "GetApplicationServerEnvironmentType"},
+ {101, nullptr, "SetApplicationServerEnvironmentType"},
+ {102, nullptr, "DeleteApplicationServerEnvironmentType"},
};
// clang-format on
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 852e71e4b..e3017451f 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -7,7 +7,7 @@
namespace Service::Sockets {
-void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
+void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
struct Parameters {
u8 use_nsd_resolve;
u32 unknown;
@@ -29,15 +29,20 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
static const FunctionInfo functions[] = {
{0, nullptr, "SetDnsAddressesPrivate"},
{1, nullptr, "GetDnsAddressPrivate"},
- {2, nullptr, "GetHostByName"},
- {3, nullptr, "GetHostByAddr"},
- {4, nullptr, "GetHostStringError"},
- {5, nullptr, "GetGaiStringError"},
- {6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
- {7, nullptr, "GetNameInfo"},
- {8, nullptr, "RequestCancelHandle"},
- {9, nullptr, "CancelSocketCall"},
- {11, nullptr, "ClearDnsIpServerAddressArray"},
+ {2, nullptr, "GetHostByNameRequest"},
+ {3, nullptr, "GetHostByAddrRequest"},
+ {4, nullptr, "GetHostStringErrorRequest"},
+ {5, nullptr, "GetGaiStringErrorRequest"},
+ {6, &SFDNSRES::GetAddrInfoRequest, "GetAddrInfoRequest"},
+ {7, nullptr, "GetNameInfoRequest"},
+ {8, nullptr, "RequestCancelHandleRequest"},
+ {9, nullptr, "CancelRequest"},
+ {10, nullptr, "GetHostByNameRequestWithOptions"},
+ {11, nullptr, "GetHostByAddrRequestWithOptions"},
+ {12, nullptr, "GetAddrInfoRequestWithOptions"},
+ {13, nullptr, "GetNameInfoRequestWithOptions"},
+ {14, nullptr, "ResolverSetOptionRequest"},
+ {15, nullptr, "ResolverGetOptionRequest"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h
index eda432903..acd3647bb 100644
--- a/src/core/hle/service/sockets/sfdnsres.h
+++ b/src/core/hle/service/sockets/sfdnsres.h
@@ -15,7 +15,7 @@ public:
~SFDNSRES() override;
private:
- void GetAddrInfo(Kernel::HLERequestContext& ctx);
+ void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp
index 08d2d306a..1d27f7906 100644
--- a/src/core/hle/service/sockets/sockets.cpp
+++ b/src/core/hle/service/sockets/sockets.cpp
@@ -10,9 +10,9 @@
namespace Service::Sockets {
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager);
- std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<BSD>(system, "bsd:s")->InstallAsService(service_manager);
+ std::make_shared<BSD>(system, "bsd:u")->InstallAsService(service_manager);
std::make_shared<BSDCFG>()->InstallAsService(service_manager);
std::make_shared<ETHC_C>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index ca8a6a7e0..89a410076 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -4,11 +4,94 @@
#pragma once
+#include "common/common_types.h"
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Service::Sockets {
+enum class Errno : u32 {
+ SUCCESS = 0,
+ BADF = 9,
+ AGAIN = 11,
+ INVAL = 22,
+ MFILE = 24,
+ NOTCONN = 107,
+};
+
+enum class Domain : u32 {
+ INET = 2,
+};
+
+enum class Type : u32 {
+ STREAM = 1,
+ DGRAM = 2,
+ RAW = 3,
+ SEQPACKET = 5,
+};
+
+enum class Protocol : u32 {
+ UNSPECIFIED = 0,
+ ICMP = 1,
+ TCP = 6,
+ UDP = 17,
+};
+
+enum class OptName : u32 {
+ REUSEADDR = 0x4,
+ BROADCAST = 0x20,
+ LINGER = 0x80,
+ SNDBUF = 0x1001,
+ RCVBUF = 0x1002,
+ SNDTIMEO = 0x1005,
+ RCVTIMEO = 0x1006,
+};
+
+enum class ShutdownHow : s32 {
+ RD = 0,
+ WR = 1,
+ RDWR = 2,
+};
+
+enum class FcntlCmd : s32 {
+ GETFL = 3,
+ SETFL = 4,
+};
+
+struct SockAddrIn {
+ u8 len;
+ u8 family;
+ u16 portno;
+ std::array<u8, 4> ip;
+ std::array<u8, 8> zeroes;
+};
+
+struct PollFD {
+ s32 fd;
+ u16 events;
+ u16 revents;
+};
+
+struct Linger {
+ u32 onoff;
+ u32 linger;
+};
+
+constexpr u16 POLL_IN = 0x01;
+constexpr u16 POLL_PRI = 0x02;
+constexpr u16 POLL_OUT = 0x04;
+constexpr u16 POLL_ERR = 0x08;
+constexpr u16 POLL_HUP = 0x10;
+constexpr u16 POLL_NVAL = 0x20;
+
+constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
+
+constexpr u32 FLAG_O_NONBLOCK = 0x800;
+
/// Registers all Sockets services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
new file mode 100644
index 000000000..2e626fd86
--- /dev/null
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -0,0 +1,165 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <utility>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/hle/service/sockets/sockets.h"
+#include "core/hle/service/sockets/sockets_translate.h"
+#include "core/network/network.h"
+
+namespace Service::Sockets {
+
+Errno Translate(Network::Errno value) {
+ switch (value) {
+ case Network::Errno::SUCCESS:
+ return Errno::SUCCESS;
+ case Network::Errno::BADF:
+ return Errno::BADF;
+ case Network::Errno::AGAIN:
+ return Errno::AGAIN;
+ case Network::Errno::INVAL:
+ return Errno::INVAL;
+ case Network::Errno::MFILE:
+ return Errno::MFILE;
+ case Network::Errno::NOTCONN:
+ return Errno::NOTCONN;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", static_cast<int>(value));
+ return Errno::SUCCESS;
+ }
+}
+
+std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
+ return {value.first, Translate(value.second)};
+}
+
+Network::Domain Translate(Domain domain) {
+ switch (domain) {
+ case Domain::INET:
+ return Network::Domain::INET;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
+ return {};
+ }
+}
+
+Domain Translate(Network::Domain domain) {
+ switch (domain) {
+ case Network::Domain::INET:
+ return Domain::INET;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
+ return {};
+ }
+}
+
+Network::Type Translate(Type type) {
+ switch (type) {
+ case Type::STREAM:
+ return Network::Type::STREAM;
+ case Type::DGRAM:
+ return Network::Type::DGRAM;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
+ }
+}
+
+Network::Protocol Translate(Type type, Protocol protocol) {
+ switch (protocol) {
+ case Protocol::UNSPECIFIED:
+ LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type");
+ switch (type) {
+ case Type::DGRAM:
+ return Network::Protocol::UDP;
+ case Type::STREAM:
+ return Network::Protocol::TCP;
+ default:
+ return Network::Protocol::TCP;
+ }
+ case Protocol::TCP:
+ return Network::Protocol::TCP;
+ case Protocol::UDP:
+ return Network::Protocol::UDP;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol));
+ return Network::Protocol::TCP;
+ }
+}
+
+u16 TranslatePollEventsToHost(u32 flags) {
+ u32 result = 0;
+ const auto translate = [&result, &flags](u32 from, u32 to) {
+ if ((flags & from) != 0) {
+ flags &= ~from;
+ result |= to;
+ }
+ };
+ translate(POLL_IN, Network::POLL_IN);
+ translate(POLL_PRI, Network::POLL_PRI);
+ translate(POLL_OUT, Network::POLL_OUT);
+ translate(POLL_ERR, Network::POLL_ERR);
+ translate(POLL_HUP, Network::POLL_HUP);
+ translate(POLL_NVAL, Network::POLL_NVAL);
+
+ UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
+ return static_cast<u16>(result);
+}
+
+u16 TranslatePollEventsToGuest(u32 flags) {
+ u32 result = 0;
+ const auto translate = [&result, &flags](u32 from, u32 to) {
+ if ((flags & from) != 0) {
+ flags &= ~from;
+ result |= to;
+ }
+ };
+
+ translate(Network::POLL_IN, POLL_IN);
+ translate(Network::POLL_PRI, POLL_PRI);
+ translate(Network::POLL_OUT, POLL_OUT);
+ translate(Network::POLL_ERR, POLL_ERR);
+ translate(Network::POLL_HUP, POLL_HUP);
+ translate(Network::POLL_NVAL, POLL_NVAL);
+
+ UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
+ return static_cast<u16>(result);
+}
+
+Network::SockAddrIn Translate(SockAddrIn value) {
+ ASSERT(value.len == 0 || value.len == sizeof(value));
+
+ return {
+ .family = Translate(static_cast<Domain>(value.family)),
+ .ip = value.ip,
+ .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
+ };
+}
+
+SockAddrIn Translate(Network::SockAddrIn value) {
+ return {
+ .len = sizeof(SockAddrIn),
+ .family = static_cast<u8>(Translate(value.family)),
+ .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
+ .ip = value.ip,
+ .zeroes = {},
+ };
+}
+
+Network::ShutdownHow Translate(ShutdownHow how) {
+ switch (how) {
+ case ShutdownHow::RD:
+ return Network::ShutdownHow::RD;
+ case ShutdownHow::WR:
+ return Network::ShutdownHow::WR;
+ case ShutdownHow::RDWR:
+ return Network::ShutdownHow::RDWR;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented how={}", static_cast<int>(how));
+ return {};
+ }
+}
+
+} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h
new file mode 100644
index 000000000..e498913d4
--- /dev/null
+++ b/src/core/hle/service/sockets/sockets_translate.h
@@ -0,0 +1,48 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+
+#include "common/common_types.h"
+#include "core/hle/service/sockets/sockets.h"
+#include "core/network/network.h"
+
+namespace Service::Sockets {
+
+/// Translate abstract errno to guest errno
+Errno Translate(Network::Errno value);
+
+/// Translate abstract return value errno pair to guest return value errno pair
+std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
+
+/// Translate guest domain to abstract domain
+Network::Domain Translate(Domain domain);
+
+/// Translate abstract domain to guest domain
+Domain Translate(Network::Domain domain);
+
+/// Translate guest type to abstract type
+Network::Type Translate(Type type);
+
+/// Translate guest protocol to abstract protocol
+Network::Protocol Translate(Type type, Protocol protocol);
+
+/// Translate abstract poll event flags to guest poll event flags
+u16 TranslatePollEventsToHost(u32 flags);
+
+/// Translate guest poll event flags to abstract poll event flags
+u16 TranslatePollEventsToGuest(u32 flags);
+
+/// Translate guest socket address structure to abstract socket address structure
+Network::SockAddrIn Translate(SockAddrIn value);
+
+/// Translate abstract socket address structure to guest socket address structure
+SockAddrIn Translate(Network::SockAddrIn value);
+
+/// Translate guest shutdown mode to abstract shutdown mode
+Network::ShutdownHow Translate(ShutdownHow how);
+
+} // namespace Service::Sockets
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index e724d4ab8..865ed3b91 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -19,7 +19,7 @@ namespace Service::SPL {
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)),
- rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
+ rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
Module::Interface::~Interface() = default;
diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp
index 70cb41905..773551464 100644
--- a/src/core/hle/service/spl/spl.cpp
+++ b/src/core/hle/service/spl/spl.cpp
@@ -9,35 +9,36 @@ namespace Service::SPL {
SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
- {1, nullptr, "UserExpMod"},
+ {1, nullptr, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
- {9, nullptr, "LoadSecureExpModKey"},
- {10, nullptr, "SecureExpMod"},
+ {9, nullptr, "ImportLotusKey"},
+ {10, nullptr, "DecryptLotusMessage"},
{11, nullptr, "IsDevelopment"},
{12, nullptr, "GenerateSpecificAesKey"},
- {13, nullptr, "DecryptPrivk"},
+ {13, nullptr, "DecryptDeviceUniqueData"},
{14, nullptr, "DecryptAesKey"},
- {15, nullptr, "DecryptAesCtr"},
+ {15, nullptr, "CryptAesCtr"},
{16, nullptr, "ComputeCmac"},
- {17, nullptr, "LoadRsaOaepKey"},
- {18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
+ {17, nullptr, "ImportEsKey"},
+ {18, nullptr, "UnwrapTitleKey"},
{19, nullptr, "LoadTitleKey"},
- {20, nullptr, "UnwrapAesWrappedTitleKey"},
- {21, nullptr, "LockAesEngine"},
- {22, nullptr, "UnlockAesEngine"},
- {23, nullptr, "GetSplWaitEvent"},
- {24, nullptr, "SetSharedData"},
- {25, nullptr, "GetSharedData"},
- {26, nullptr, "ImportSslRsaKey"},
- {27, nullptr, "SecureExpModWithSslKey"},
- {28, nullptr, "ImportEsRsaKey"},
- {29, nullptr, "SecureExpModWithEsKey"},
- {30, nullptr, "EncryptManuRsaKeyForImport"},
- {31, nullptr, "GetPackage2Hash"},
+ {20, nullptr, "PrepareEsCommonKey"},
+ {21, nullptr, "AllocateAesKeyslot"},
+ {22, nullptr, "DeallocateAesKeySlot"},
+ {23, nullptr, "GetAesKeyslotAvailableEvent"},
+ {24, nullptr, "SetBootReason"},
+ {25, nullptr, "GetBootReason"},
+ {26, nullptr, "DecryptAndStoreSslClientCertKey"},
+ {27, nullptr, "ModularExponentiateWithSslClientCertKey"},
+ {28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
+ {29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
+ {30, nullptr, "ReencryptDeviceUniqueData "},
+ {31, nullptr, "PrepareEsArchiveKey"}, // This is also GetPackage2Hash?
+ {32, nullptr, "LoadPreparedAesKey"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index f509653a3..ba8fd6152 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -29,7 +29,7 @@ Time::Time(std::shared_ptr<Module> module, Core::System& system, const char* nam
{300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
{401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
- {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
+ {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
{501, &Time::CalculateSpanBetween, "CalculateSpanBetween"},
};
// clang-format on
diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h
index 3f505c37c..c993bdf79 100644
--- a/src/core/hle/service/time/standard_network_system_clock_core.h
+++ b/src/core/hle/service/time/standard_network_system_clock_core.h
@@ -23,7 +23,7 @@ public:
standard_network_clock_sufficient_accuracy = value;
}
- bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) {
+ bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const {
SystemClockContext context{};
if (GetClockContext(system, context) != RESULT_SUCCESS) {
return {};
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
index 1575f0b49..59a272f4a 100644
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -11,9 +11,8 @@
namespace Service::Time::Clock {
TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
- const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
- Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
- Core::Hardware::CNTFREQ)};
+ const TimeSpanType ticks_time_span{
+ TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
index 84af3d105..d80a2385f 100644
--- a/src/core/hle/service/time/steady_clock_core.h
+++ b/src/core/hle/service/time/steady_clock_core.h
@@ -16,6 +16,7 @@ namespace Service::Time::Clock {
class SteadyClockCore {
public:
SteadyClockCore() = default;
+ virtual ~SteadyClockCore() = default;
const Common::UUID& GetClockSourceId() const {
return clock_source_id;
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
index 6260de6c3..2b0fa7e75 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.h
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -20,7 +20,7 @@ namespace Service::Time::Clock {
class SystemClockContextUpdateCallback {
public:
SystemClockContextUpdateCallback();
- ~SystemClockContextUpdateCallback();
+ virtual ~SystemClockContextUpdateCallback();
bool NeedUpdate(const SystemClockContext& value) const;
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
index 1a3ab8cfa..d31d4e2ca 100644
--- a/src/core/hle/service/time/system_clock_core.cpp
+++ b/src/core/hle/service/time/system_clock_core.cpp
@@ -9,7 +9,7 @@
namespace Service::Time::Clock {
SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core)
- : steady_clock_core{steady_clock_core}, is_initialized{} {
+ : steady_clock_core{steady_clock_core} {
context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
}
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
index 54407a6c5..608dd3b2e 100644
--- a/src/core/hle/service/time/system_clock_core.h
+++ b/src/core/hle/service/time/system_clock_core.h
@@ -22,7 +22,7 @@ class SystemClockContextUpdateCallback;
class SystemClockCore {
public:
explicit SystemClockCore(SteadyClockCore& steady_clock_core);
- ~SystemClockCore();
+ virtual ~SystemClockCore();
SteadyClockCore& GetSteadyClockCore() const {
return steady_clock_core;
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
index 44d5bc651..8baaa2a6a 100644
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -11,9 +11,8 @@
namespace Service::Time::Clock {
SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
- const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
- Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
- Core::Hardware::CNTFREQ)};
+ const TimeSpanType ticks_time_span{
+ TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
return {ticks_time_span.ToSeconds(), GetClockSourceId()};
}
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index ce859f18d..7d0474e0b 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -10,6 +10,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/service/time/interface.h"
#include "core/hle/service/time/time.h"
@@ -20,8 +21,8 @@ namespace Service::Time {
class ISystemClock final : public ServiceFramework<ISystemClock> {
public:
- ISystemClock(Clock::SystemClockCore& clock_core)
- : ServiceFramework("ISystemClock"), clock_core{clock_core} {
+ explicit ISystemClock(Clock::SystemClockCore& clock_core, Core::System& system)
+ : ServiceFramework("ISystemClock"), clock_core{clock_core}, system{system} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
@@ -46,9 +47,8 @@ private:
}
s64 posix_time{};
- if (const ResultCode result{
- clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)};
- result != RESULT_SUCCESS) {
+ if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -69,9 +69,8 @@ private:
}
Clock::SystemClockContext system_clock_context{};
- if (const ResultCode result{
- clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)};
- result != RESULT_SUCCESS) {
+ if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -83,14 +82,22 @@ private:
}
Clock::SystemClockCore& clock_core;
+ Core::System& system;
};
class ISteadyClock final : public ServiceFramework<ISteadyClock> {
public:
- ISteadyClock(Clock::SteadyClockCore& clock_core)
- : ServiceFramework("ISteadyClock"), clock_core{clock_core} {
+ explicit ISteadyClock(Clock::SteadyClockCore& clock_core, Core::System& system)
+ : ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
static const FunctionInfo functions[] = {
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
+ {2, nullptr, "GetTestOffset"},
+ {3, nullptr, "SetTestOffset"},
+ {100, nullptr, "GetRtcValue"},
+ {101, nullptr, "IsRtcResetDetected"},
+ {102, nullptr, "GetSetupResultValue"},
+ {200, nullptr, "GetInternalOffset"},
+ {201, nullptr, "SetInternalOffset"},
};
RegisterHandlers(functions);
}
@@ -105,21 +112,21 @@ private:
return;
}
- const Clock::SteadyClockTimePoint time_point{
- clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+ const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)};
IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(time_point);
}
Clock::SteadyClockCore& clock_core;
+ Core::System& system;
};
ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
Kernel::Thread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
- auto& time_manager{module->GetTimeManager()};
+ auto& time_manager{system.GetTimeManager()};
clock_snapshot.is_automatic_correction_enabled =
time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
@@ -134,7 +141,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
}
const auto current_time_point{
- time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())};
+ time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
result != RESULT_SUCCESS) {
@@ -176,41 +183,44 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore());
+ rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(),
+ system);
}
void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore());
+ rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(),
+ system);
}
void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore());
+ rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system);
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager());
+ rb.PushIpcInterface<ITimeZoneService>(system.GetTimeManager().GetTimeZoneContentManager());
}
void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore());
+ rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(),
+ system);
}
void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
- auto& clock_core{module->GetTimeManager().GetStandardNetworkSystemClockCore()};
+ auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
@@ -219,7 +229,7 @@ void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
- auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()};
+ auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()};
if (!steady_clock_core.IsInitialized()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_UNINITIALIZED_CLOCK);
@@ -228,13 +238,11 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe
IPC::RequestParser rp{ctx};
const auto context{rp.PopRaw<Clock::SystemClockContext>()};
- const auto current_time_point{
- steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+ const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
- const auto ticks{Clock::TimeSpanType::FromTicks(
- Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
- Core::Hardware::CNTFREQ)};
+ const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(),
+ Core::Hardware::CNTFREQ)};
const s64 base_time_point{context.offset + current_time_point.time_point -
ticks.ToSeconds()};
IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
@@ -254,18 +262,18 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
Clock::SystemClockContext user_context{};
if (const ResultCode result{
- module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
- Core::System::GetInstance(), user_context)};
- result != RESULT_SUCCESS) {
+ system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
+ user_context)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
Clock::SystemClockContext network_context{};
if (const ResultCode result{
- module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
- Core::System::GetInstance(), network_context)};
- result != RESULT_SUCCESS) {
+ system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
+ system, network_context)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -274,7 +282,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
Clock::ClockSnapshot clock_snapshot{};
if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
- result != RESULT_SUCCESS) {
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -282,7 +290,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
+ ctx.WriteBuffer(clock_snapshot);
}
void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
@@ -305,7 +313,30 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
+ ctx.WriteBuffer(clock_snapshot);
+}
+
+void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
+ Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>();
+ const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>();
+
+ auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset -
+ snapshot_a.user_context.offset)};
+
+ if ((snapshot_b.user_context.steady_time_point.clock_source_id !=
+ snapshot_a.user_context.steady_time_point.clock_source_id) ||
+ (snapshot_b.is_automatic_correction_enabled &&
+ snapshot_a.is_automatic_correction_enabled)) {
+ time_span_type.nanoseconds = 0;
+ }
+
+ IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(time_span_type.nanoseconds);
}
void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
@@ -341,7 +372,7 @@ void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& c
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder());
+ rb.PushCopyObjects(SharedFrom(&system.Kernel().GetTimeSharedMem()));
}
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
@@ -350,7 +381,7 @@ Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& syste
Module::Interface::~Interface() = default;
void InstallInterfaces(Core::System& system) {
- auto module{std::make_shared<Module>(system)};
+ auto module{std::make_shared<Module>()};
std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 351988468..49f4aac0a 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -16,7 +16,7 @@ namespace Service::Time {
class Module final {
public:
- Module(Core::System& system) : time_manager{system} {}
+ Module() = default;
class Interface : public ServiceFramework<Interface> {
public:
@@ -32,6 +32,7 @@ public:
void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx);
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
void GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx);
+ void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
void CalculateSpanBetween(Kernel::HLERequestContext& ctx);
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
@@ -45,13 +46,6 @@ public:
std::shared_ptr<Module> module;
Core::System& system;
};
-
- TimeManager& GetTimeManager() {
- return time_manager;
- }
-
-private:
- TimeManager time_manager;
};
/// Registers all Time services with the specified service manager.
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index 9d6c55865..858623e2b 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -5,6 +5,7 @@
#include <chrono>
#include <ctime>
+#include "common/time_zone.h"
#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
#include "core/hle/service/time/local_system_clock_context_writer.h"
#include "core/hle/service/time/network_system_clock_context_writer.h"
@@ -22,116 +23,281 @@ static std::chrono::seconds GetSecondsSinceEpoch() {
}
static s64 GetExternalRtcValue() {
- return GetSecondsSinceEpoch().count();
-}
-
-TimeManager::TimeManager(Core::System& system)
- : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
- standard_network_system_clock_core{standard_steady_clock_core},
- standard_user_system_clock_core{standard_local_system_clock_core,
- standard_network_system_clock_core, system},
- ephemeral_network_system_clock_core{tick_based_steady_clock_core},
- local_system_clock_context_writer{
- std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
- network_system_clock_context_writer{
- std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
- ephemeral_network_system_clock_context_writer{
- std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
- time_zone_content_manager{*this, system} {
-
- const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
- SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
- SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
- SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
- SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
- SetupEphemeralNetworkSystemClock();
+ return GetSecondsSinceEpoch().count() + TimeManager::GetExternalTimeZoneOffset();
}
-TimeManager::~TimeManager() = default;
+struct TimeManager::Impl final {
+ explicit Impl(Core::System& system)
+ : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
+ standard_network_system_clock_core{standard_steady_clock_core},
+ standard_user_system_clock_core{standard_local_system_clock_core,
+ standard_network_system_clock_core, system},
+ ephemeral_network_system_clock_core{tick_based_steady_clock_core},
+ local_system_clock_context_writer{
+ std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
+ network_system_clock_context_writer{
+ std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
+ ephemeral_network_system_clock_context_writer{
+ std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
+ time_zone_content_manager{system} {
-void TimeManager::SetupTimeZoneManager(std::string location_name,
- Clock::SteadyClockTimePoint time_zone_updated_time_point,
- std::size_t total_location_name_count,
- u128 time_zone_rule_version,
- FileSys::VirtualFile& vfs_file) {
- if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
- location_name, vfs_file) != RESULT_SUCCESS) {
- UNREACHABLE();
- return;
- }
-
- time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
- time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
- total_location_name_count);
- time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
- time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
-}
-
-void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
- Clock::TimeSpanType setup_value,
- Clock::TimeSpanType internal_offset,
- bool is_rtc_reset_detected) {
- standard_steady_clock_core.SetClockSourceId(clock_source_id);
- standard_steady_clock_core.SetSetupValue(setup_value);
- standard_steady_clock_core.SetInternalOffset(internal_offset);
- standard_steady_clock_core.MarkAsInitialized();
-
- const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
- shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
-}
-
-void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
- Clock::SystemClockContext clock_context,
- s64 posix_time) {
- standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);
-
- const auto current_time_point{
- standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
- if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
- standard_local_system_clock_core.SetSystemClockContext(clock_context);
- } else {
- if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
+ const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
+ SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
+ SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
+ SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
+ SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
+ SetupEphemeralNetworkSystemClock();
+ }
+
+ ~Impl() = default;
+
+ Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
+ return standard_steady_clock_core;
+ }
+
+ const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
+ return standard_steady_clock_core;
+ }
+
+ Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
+ return standard_local_system_clock_core;
+ }
+
+ const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
+ return standard_local_system_clock_core;
+ }
+
+ Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
+ return standard_network_system_clock_core;
+ }
+
+ const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
+ return standard_network_system_clock_core;
+ }
+
+ Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
+ return standard_user_system_clock_core;
+ }
+
+ const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
+ return standard_user_system_clock_core;
+ }
+
+ TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
+ return time_zone_content_manager;
+ }
+
+ const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
+ return time_zone_content_manager;
+ }
+
+ SharedMemory& GetSharedMemory() {
+ return shared_memory;
+ }
+
+ const SharedMemory& GetSharedMemory() const {
+ return shared_memory;
+ }
+
+ void SetupTimeZoneManager(std::string location_name,
+ Clock::SteadyClockTimePoint time_zone_updated_time_point,
+ std::size_t total_location_name_count, u128 time_zone_rule_version,
+ FileSys::VirtualFile& vfs_file) {
+ if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
+ location_name, vfs_file) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
+
+ time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
+ time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
+ total_location_name_count);
+ time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(
+ time_zone_rule_version);
+ time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
}
- standard_local_system_clock_core.MarkAsInitialized();
-}
+ static s64 GetExternalTimeZoneOffset() {
+ // With "auto" timezone setting, we use the external system's timezone offset
+ if (Settings::GetTimeZoneString() == "auto") {
+ return Common::TimeZone::GetCurrentOffsetSeconds().count();
+ }
+ return 0;
+ }
-void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
- Clock::TimeSpanType sufficient_accuracy) {
- standard_network_system_clock_core.SetUpdateCallbackInstance(
- network_system_clock_context_writer);
+ void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
+ Clock::TimeSpanType setup_value,
+ Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
+ standard_steady_clock_core.SetClockSourceId(clock_source_id);
+ standard_steady_clock_core.SetSetupValue(setup_value);
+ standard_steady_clock_core.SetInternalOffset(internal_offset);
+ standard_steady_clock_core.MarkAsInitialized();
- if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
- UNREACHABLE();
- return;
+ const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
+ shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
}
- standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
- sufficient_accuracy);
- standard_network_system_clock_core.MarkAsInitialized();
-}
+ void SetupStandardLocalSystemClock(Core::System& system,
+ Clock::SystemClockContext clock_context, s64 posix_time) {
+ standard_local_system_clock_core.SetUpdateCallbackInstance(
+ local_system_clock_context_writer);
+
+ const auto current_time_point{
+ standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
+ if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
+ standard_local_system_clock_core.SetSystemClockContext(clock_context);
+ } else {
+ if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) !=
+ RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+ }
+
+ standard_local_system_clock_core.MarkAsInitialized();
+ }
+
+ void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
+ Clock::TimeSpanType sufficient_accuracy) {
+ standard_network_system_clock_core.SetUpdateCallbackInstance(
+ network_system_clock_context_writer);
+
+ if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
+ RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+
+ standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
+ sufficient_accuracy);
+ standard_network_system_clock_core.MarkAsInitialized();
+ }
+
+ void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
+ Clock::SteadyClockTimePoint steady_clock_time_point) {
+ if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
+ system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+
+ standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
+ standard_user_system_clock_core.MarkAsInitialized();
+ shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
+ }
-void TimeManager::SetupStandardUserSystemClock(
- Core::System& system, bool is_automatic_correction_enabled,
- Clock::SteadyClockTimePoint steady_clock_time_point) {
- if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
- system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
- UNREACHABLE();
- return;
+ void SetupEphemeralNetworkSystemClock() {
+ ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
+ ephemeral_network_system_clock_context_writer);
+ ephemeral_network_system_clock_core.MarkAsInitialized();
}
- standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
- standard_user_system_clock_core.MarkAsInitialized();
- shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
+ void UpdateLocalSystemClockTime(Core::System& system, s64 posix_time) {
+ const auto timespan{Service::Time::Clock::TimeSpanType::FromSeconds(posix_time)};
+ if (GetStandardLocalSystemClockCore()
+ .SetCurrentTime(system, timespan.ToSeconds())
+ .IsError()) {
+ UNREACHABLE();
+ return;
+ }
+ }
+
+ SharedMemory shared_memory;
+
+ Clock::StandardSteadyClockCore standard_steady_clock_core;
+ Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
+ Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
+ Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
+ Clock::StandardUserSystemClockCore standard_user_system_clock_core;
+ Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
+
+ std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
+ std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
+ std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
+ ephemeral_network_system_clock_context_writer;
+
+ TimeZone::TimeZoneContentManager time_zone_content_manager;
+};
+
+TimeManager::TimeManager(Core::System& system) : system{system} {}
+
+TimeManager::~TimeManager() = default;
+
+void TimeManager::Initialize() {
+ impl = std::make_unique<Impl>(system);
+
+ // Time zones can only be initialized after impl is valid
+ impl->time_zone_content_manager.Initialize(*this);
}
-void TimeManager::SetupEphemeralNetworkSystemClock() {
- ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
- ephemeral_network_system_clock_context_writer);
- ephemeral_network_system_clock_core.MarkAsInitialized();
+Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() {
+ return impl->standard_steady_clock_core;
+}
+
+const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const {
+ return impl->standard_steady_clock_core;
+}
+
+Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() {
+ return impl->standard_local_system_clock_core;
+}
+
+const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const {
+ return impl->standard_local_system_clock_core;
+}
+
+Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() {
+ return impl->standard_network_system_clock_core;
+}
+
+const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore()
+ const {
+ return impl->standard_network_system_clock_core;
+}
+
+Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() {
+ return impl->standard_user_system_clock_core;
+}
+
+const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const {
+ return impl->standard_user_system_clock_core;
+}
+
+TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() {
+ return impl->time_zone_content_manager;
+}
+
+const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const {
+ return impl->time_zone_content_manager;
+}
+
+SharedMemory& TimeManager::GetSharedMemory() {
+ return impl->shared_memory;
+}
+
+const SharedMemory& TimeManager::GetSharedMemory() const {
+ return impl->shared_memory;
+}
+
+void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
+ impl->UpdateLocalSystemClockTime(system, posix_time);
+}
+
+void TimeManager::SetupTimeZoneManager(std::string location_name,
+ Clock::SteadyClockTimePoint time_zone_updated_time_point,
+ std::size_t total_location_name_count,
+ u128 time_zone_rule_version,
+ FileSys::VirtualFile& vfs_file) {
+ impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point,
+ total_location_name_count, time_zone_rule_version, vfs_file);
+}
+
+/*static*/ s64 TimeManager::GetExternalTimeZoneOffset() {
+ // With "auto" timezone setting, we use the external system's timezone offset
+ if (Settings::GetTimeZoneString() == "auto") {
+ return Common::TimeZone::GetCurrentOffsetSeconds().count();
+ }
+ return 0;
}
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
index 8e65f0d22..993c7c288 100644
--- a/src/core/hle/service/time/time_manager.h
+++ b/src/core/hle/service/time/time_manager.h
@@ -5,6 +5,7 @@
#pragma once
#include "common/common_types.h"
+#include "common/time_zone.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/service/time/clock_types.h"
#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
@@ -32,86 +33,46 @@ public:
explicit TimeManager(Core::System& system);
~TimeManager();
- Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
- return standard_steady_clock_core;
- }
+ void Initialize();
- const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
- return standard_steady_clock_core;
- }
+ Clock::StandardSteadyClockCore& GetStandardSteadyClockCore();
- Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
- return standard_local_system_clock_core;
- }
+ const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const;
- const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
- return standard_local_system_clock_core;
- }
+ Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore();
- Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
- return standard_network_system_clock_core;
- }
+ const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const;
- const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
- return standard_network_system_clock_core;
- }
+ Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore();
- Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
- return standard_user_system_clock_core;
- }
+ const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const;
- const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
- return standard_user_system_clock_core;
- }
+ Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore();
- TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
- return time_zone_content_manager;
- }
+ const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const;
- const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
- return time_zone_content_manager;
- }
+ TimeZone::TimeZoneContentManager& GetTimeZoneContentManager();
- SharedMemory& GetSharedMemory() {
- return shared_memory;
- }
+ const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const;
- const SharedMemory& GetSharedMemory() const {
- return shared_memory;
- }
+ void UpdateLocalSystemClockTime(s64 posix_time);
+
+ SharedMemory& GetSharedMemory();
+
+ const SharedMemory& GetSharedMemory() const;
void SetupTimeZoneManager(std::string location_name,
Clock::SteadyClockTimePoint time_zone_updated_time_point,
std::size_t total_location_name_count, u128 time_zone_rule_version,
FileSys::VirtualFile& vfs_file);
+ static s64 GetExternalTimeZoneOffset();
+
private:
- void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
- Clock::TimeSpanType setup_value,
- Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected);
- void SetupStandardLocalSystemClock(Core::System& system,
- Clock::SystemClockContext clock_context, s64 posix_time);
- void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
- Clock::TimeSpanType sufficient_accuracy);
- void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
- Clock::SteadyClockTimePoint steady_clock_time_point);
- void SetupEphemeralNetworkSystemClock();
-
- SharedMemory shared_memory;
-
- Clock::StandardSteadyClockCore standard_steady_clock_core;
- Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
- Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
- Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
- Clock::StandardUserSystemClockCore standard_user_system_clock_core;
- Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
-
- std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
- std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
- std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
- ephemeral_network_system_clock_context_writer;
-
- TimeZone::TimeZoneContentManager time_zone_content_manager;
+ Core::System& system;
+
+ struct Impl;
+ std::unique_ptr<Impl> impl;
};
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index fdaef233f..e0ae9f874 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -6,6 +6,7 @@
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hardware_properties.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/service/time/clock_types.h"
#include "core/hle/service/time/steady_clock_core.h"
#include "core/hle/service/time/time_sharedmemory.h"
@@ -15,9 +16,7 @@ namespace Service::Time {
static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
SharedMemory::SharedMemory(Core::System& system) : system(system) {
- shared_memory_holder = Kernel::SharedMemory::Create(
- system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory");
+ shared_memory_holder = SharedFrom(&system.Kernel().GetTimeSharedMem());
std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE);
}
@@ -31,8 +30,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system,
const Common::UUID& clock_source_id,
Clock::TimeSpanType current_time_point) {
const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
- Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
- Core::Hardware::CNTFREQ)};
+ system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
const Clock::SteadyClockContext context{
static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
clock_source_id};
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index 78d4acd95..4177d0a41 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -5,6 +5,7 @@
#include <sstream>
#include "common/logging/log.h"
+#include "common/time_zone.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
@@ -14,6 +15,7 @@
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/time/time_manager.h"
#include "core/hle/service/time/time_zone_content_manager.h"
+#include "core/settings.h"
namespace Service::Time::TimeZone {
@@ -66,12 +68,23 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
return location_name_cache;
}
-TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
- : system{system}, location_name_cache{BuildLocationNameCache(system)} {
- if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) {
+TimeZoneContentManager::TimeZoneContentManager(Core::System& system)
+ : system{system}, location_name_cache{BuildLocationNameCache(system)} {}
+
+void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
+ std::string location_name;
+ const auto timezone_setting = Settings::GetTimeZoneString();
+ if (timezone_setting == "auto" || timezone_setting == "default") {
+ location_name = Common::TimeZone::GetDefaultTimeZone();
+ } else {
+ location_name = timezone_setting;
+ }
+
+ if (FileSys::VirtualFile vfs_file;
+ GetTimeZoneInfoFile(location_name, vfs_file) == RESULT_SUCCESS) {
const auto time_point{
time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
- time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {},
+ time_manager.SetupTimeZoneManager(location_name, time_point, location_name_cache.size(), {},
vfs_file);
} else {
time_zone_manager.MarkAsInitialized();
@@ -114,6 +127,12 @@ ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& locati
vfs_file = zoneinfo_dir->GetFile(location_name);
if (!vfs_file) {
+ LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
+ time_zone_binary_titleid, location_name);
+ vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone());
+ }
+
+ if (!vfs_file) {
LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
location_name);
return ERROR_TIME_NOT_FOUND;
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
index 4f302c3b9..52dd1a020 100644
--- a/src/core/hle/service/time/time_zone_content_manager.h
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -21,7 +21,9 @@ namespace Service::Time::TimeZone {
class TimeZoneContentManager final {
public:
- TimeZoneContentManager(TimeManager& time_manager, Core::System& system);
+ explicit TimeZoneContentManager(Core::System& system);
+
+ void Initialize(TimeManager& time_manager);
TimeZoneManager& GetTimeZoneManager() {
return time_zone_manager;
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 07b553a43..bdf0439f2 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -309,7 +309,7 @@ static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
offset = GetTZName(name, offset);
std_len = offset;
}
- if (!std_len) {
+ if (std_len == 0) {
return {};
}
if (!GetOffset(name, offset, std_offset)) {
@@ -320,7 +320,7 @@ static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
int dest_len{};
int dest_offset{};
const char* dest_name{name + offset};
- if (rule.chars.size() < char_count) {
+ if (rule.chars.size() < std::size_t(char_count)) {
return {};
}
@@ -343,7 +343,7 @@ static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
return {};
}
char_count += dest_len + 1;
- if (rule.chars.size() < char_count) {
+ if (rule.chars.size() < std::size_t(char_count)) {
return {};
}
if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') {
@@ -414,7 +414,7 @@ static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
if (is_reversed ||
(start_time < end_time &&
(end_time - start_time < (year_seconds + (std_offset - dest_offset))))) {
- if (rule.ats.size() - 2 < time_count) {
+ if (rule.ats.size() - 2 < std::size_t(time_count)) {
break;
}
@@ -518,8 +518,8 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
constexpr s32 time_zone_max_leaps{50};
constexpr s32 time_zone_max_chars{50};
if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
- 0 < header.type_count && header.type_count < time_zone_rule.ttis.size() &&
- 0 <= header.time_count && header.time_count < time_zone_rule.ats.size() &&
+ 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) &&
+ 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) &&
0 <= header.char_count && header.char_count < time_zone_max_chars &&
(header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&
(header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) {
@@ -609,7 +609,7 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
}
const u64 position{(read_offset - sizeof(TzifHeader))};
- const std::size_t bytes_read{vfs_file->GetSize() - sizeof(TzifHeader) - position};
+ const s64 bytes_read = s64(vfs_file->GetSize() - sizeof(TzifHeader) - position);
if (bytes_read < 0) {
return {};
}
@@ -621,11 +621,11 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
std::array<char, time_zone_name_max + 1> temp_name{};
vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset);
if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' &&
- time_zone_rule.type_count + 2 <= time_zone_rule.ttis.size()) {
+ std::size_t(time_zone_rule.type_count) + 2 <= time_zone_rule.ttis.size()) {
temp_name[bytes_read - 1] = '\0';
std::array<char, time_zone_name_max> name{};
- std::memcpy(name.data(), temp_name.data() + 1, bytes_read - 1);
+ std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1));
TimeZoneRule temp_rule;
if (ParsePosixName(name.data(), temp_rule)) {
@@ -820,7 +820,10 @@ static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, Calend
const ResultCode result{
ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)};
calendar.time.year = static_cast<s16>(calendar_time.year);
- calendar.time.month = calendar_time.month + 1; // Internal impl. uses 0-indexed month
+
+ // Internal impl. uses 0-indexed month
+ calendar.time.month = static_cast<s8>(calendar_time.month + 1);
+
calendar.time.day = calendar_time.day;
calendar.time.hour = calendar_time.hour;
calendar.time.minute = calendar_time.minute;
@@ -872,13 +875,15 @@ ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
const CalendarTime& calendar_time, s64& posix_time) const {
posix_time = 0;
- CalendarTimeInternal internal_time{};
- internal_time.year = calendar_time.year;
- internal_time.month = calendar_time.month - 1; // Internal impl. uses 0-indexed month
- internal_time.day = calendar_time.day;
- internal_time.hour = calendar_time.hour;
- internal_time.minute = calendar_time.minute;
- internal_time.second = calendar_time.second;
+ CalendarTimeInternal internal_time{
+ .year = calendar_time.year,
+ // Internal impl. uses 0-indexed month
+ .month = static_cast<s8>(calendar_time.month - 1),
+ .day = calendar_time.day,
+ .hour = calendar_time.hour,
+ .minute = calendar_time.minute,
+ .second = calendar_time.second,
+ };
s32 hour{internal_time.hour};
s32 minute{internal_time.minute};
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
index db57ae069..ff3a10b3e 100644
--- a/src/core/hle/service/time/time_zone_service.cpp
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -142,7 +142,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Number of times we're returning
- ctx.WriteBuffer(&posix_time, sizeof(s64));
+ ctx.WriteBuffer(posix_time);
}
void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
@@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Number of times we're returning
- ctx.WriteBuffer(&posix_time, sizeof(s64));
+ ctx.WriteBuffer(posix_time);
}
} // namespace Service::Time
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index 58a9845fc..d033f8603 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -20,7 +20,7 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "GetDsEndpoint"},
{1, nullptr, "GetSetupEvent"},
- {2, nullptr, "Unknown"},
+ {2, nullptr, "Unknown2"},
{3, nullptr, "EnableInterface"},
{4, nullptr, "DisableInterface"},
{5, nullptr, "CtrlInPostBufferAsync"},
@@ -55,6 +55,7 @@ public:
{9, nullptr, "SetBinaryObjectStore"},
{10, nullptr, "Enable"},
{11, nullptr, "Disable"},
+ {12, nullptr, "Unknown12"},
};
// clang-format on
@@ -69,13 +70,13 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "Open"},
{1, nullptr, "Close"},
- {2, nullptr, "Unknown1"},
+ {2, nullptr, "Unknown2"},
{3, nullptr, "Populate"},
{4, nullptr, "PostBufferAsync"},
{5, nullptr, "GetXferReport"},
{6, nullptr, "PostBufferMultiAsync"},
- {7, nullptr, "Unknown3"},
- {8, nullptr, "Unknown4"},
+ {7, nullptr, "Unknown7"},
+ {8, nullptr, "Unknown8"},
};
// clang-format on
@@ -88,13 +89,13 @@ public:
explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
+ {0, nullptr, "Unknown0"},
{1, nullptr, "SetInterface"},
{2, nullptr, "GetInterface"},
{3, nullptr, "GetAlternateInterface"},
{4, nullptr, "GetCurrentFrame"},
{5, nullptr, "CtrlXferAsync"},
- {6, nullptr, "Unknown2"},
+ {6, nullptr, "Unknown6"},
{7, nullptr, "GetCtrlXferReport"},
{8, nullptr, "ResetDevice"},
{9, nullptr, "OpenUsbEp"},
@@ -118,7 +119,7 @@ public:
{5, nullptr, "DestroyInterfaceAvailableEvent"},
{6, nullptr, "GetInterfaceStateChangeEvent"},
{7, nullptr, "AcquireUsbIf"},
- {8, nullptr, "Unknown1"},
+ {8, nullptr, "Unknown8"},
};
// clang-format on
@@ -179,8 +180,8 @@ public:
{4, nullptr, "GetFwRevision"},
{5, nullptr, "GetManufacturerId"},
{6, nullptr, "GetDeviceId"},
- {7, nullptr, "Unknown1"},
- {8, nullptr, "Unknown2"},
+ {7, nullptr, "Unknown7"},
+ {8, nullptr, "Unknown8"},
};
// clang-format on
@@ -215,12 +216,12 @@ public:
explicit USB_PM() : ServiceFramework{"usb:pm"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
- {4, nullptr, "Unknown5"},
- {5, nullptr, "Unknown6"},
+ {0, nullptr, "Unknown0"},
+ {1, nullptr, "Unknown1"},
+ {2, nullptr, "Unknown2"},
+ {3, nullptr, "Unknown3"},
+ {4, nullptr, "Unknown4"},
+ {5, nullptr, "Unknown5"},
};
// clang-format on
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index fdc62d05b..55e00dd93 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -101,8 +101,8 @@ public:
}
std::u16string ReadInterfaceToken() {
- u32 unknown = Read<u32_le>();
- u32 length = Read<u32_le>();
+ [[maybe_unused]] const u32 unknown = Read<u32_le>();
+ const u32 length = Read<u32_le>();
std::u16string token{};
@@ -159,7 +159,7 @@ public:
header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
header.data_offset = sizeof(Header);
header.objects_size = 4;
- header.objects_offset = sizeof(Header) + header.data_size;
+ header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
std::memcpy(buffer.data(), &header, sizeof(Header));
return buffer;
@@ -215,10 +215,9 @@ public:
explicit IGBPConnectRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPConnectRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -267,7 +266,7 @@ protected:
private:
struct Data {
- u32_le unk_0;
+ u32_le unk_0{};
};
Data data{};
@@ -279,10 +278,9 @@ public:
: Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPSetPreallocatedBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
buffer = Read<NVFlinger::IGBPBuffer>();
}
@@ -306,15 +304,40 @@ protected:
}
};
+class IGBPCancelBufferRequestParcel : public Parcel {
+public:
+ explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
+ Deserialize();
+ }
+
+ void DeserializeData() override {
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
+ data = Read<Data>();
+ }
+
+ struct Data {
+ u32_le slot;
+ Service::Nvidia::MultiFence multi_fence;
+ };
+
+ Data data;
+};
+
+class IGBPCancelBufferResponseParcel : public Parcel {
+protected:
+ void SerializeData() override {
+ Write<u32>(0); // Success
+ }
+};
+
class IGBPDequeueBufferRequestParcel : public Parcel {
public:
explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPDequeueBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -333,7 +356,6 @@ class IGBPDequeueBufferResponseParcel : public Parcel {
public:
explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence)
: slot(slot), multi_fence(multi_fence) {}
- ~IGBPDequeueBufferResponseParcel() override = default;
protected:
void SerializeData() override {
@@ -352,10 +374,9 @@ public:
explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPRequestBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
slot = Read<u32_le>();
}
@@ -384,10 +405,9 @@ public:
explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPQueueBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -447,10 +467,9 @@ public:
explicit IGBPQueryRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPQueryRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
type = Read<u32_le>();
}
@@ -511,6 +530,7 @@ private:
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
static_cast<u32>(transaction), flags);
+ const auto guard = nv_flinger->Lock();
auto& buffer_queue = nv_flinger->FindBufferQueue(id);
switch (transaction) {
@@ -518,9 +538,9 @@ private:
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
IGBPConnectResponseParcel response{
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
- Settings::values.resolution_factor),
+ Settings::values.resolution_factor.GetValue()),
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
- Settings::values.resolution_factor)};
+ Settings::values.resolution_factor.GetValue())};
ctx.WriteBuffer(response.Serialize());
break;
}
@@ -547,9 +567,10 @@ private:
// Wait the current thread until a buffer becomes available
ctx.SleepClientThread(
"IHOSBinderDriver::DequeueBuffer", UINT64_MAX,
- [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
- Kernel::ThreadWakeupReason reason) {
+ [=, this](std::shared_ptr<Kernel::Thread> thread,
+ Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
+ const auto guard = nv_flinger->Lock();
auto& buffer_queue = nv_flinger->FindBufferQueue(id);
auto result = buffer_queue.DequeueBuffer(width, height);
ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
@@ -594,7 +615,12 @@ private:
break;
}
case TransactionId::CancelBuffer: {
- LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
+ IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
+
+ buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
+
+ IGBPCancelBufferResponseParcel response{};
+ ctx.WriteBuffer(response.Serialize());
break;
}
case TransactionId::Disconnect: {
@@ -614,6 +640,14 @@ private:
ctx.WriteBuffer(response.Serialize());
break;
}
+ case TransactionId::SetBufferCount: {
+ LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount");
+ [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
+
+ IGBPEmptyResponseParcel response{};
+ ctx.WriteBuffer(response.Serialize());
+ break;
+ }
default:
ASSERT_MSG(false, "Unimplemented");
}
@@ -690,6 +724,7 @@ public:
{3215, nullptr, "SetDisplayGamma"},
{3216, nullptr, "GetDisplayCmuLuma"},
{3217, nullptr, "SetDisplayCmuLuma"},
+ {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
{8225, nullptr, "GetSharedBufferMemoryHandleId"},
{8250, nullptr, "OpenSharedLayer"},
{8251, nullptr, "CloseSharedLayer"},
@@ -736,16 +771,16 @@ private:
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
- if (Settings::values.use_docked_mode) {
+ if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
} else {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
}
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
@@ -775,6 +810,7 @@ public:
{2300, nullptr, "AcquireLayerTexturePresentingEvent"},
{2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
{2302, nullptr, "GetDisplayHotplugEvent"},
+ {2303, nullptr, "GetDisplayModeChangedEvent"},
{2402, nullptr, "GetDisplayHotplugState"},
{2501, nullptr, "GetCompositorErrorInfo"},
{2601, nullptr, "GetDisplayErrorEvent"},
@@ -859,6 +895,7 @@ private:
const auto layer_id = nv_flinger->CreateLayer(display);
if (!layer_id) {
+ LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
@@ -975,6 +1012,7 @@ private:
const auto display_id = nv_flinger->OpenDisplay(name);
if (!display_id) {
+ LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
@@ -1017,9 +1055,9 @@ private:
// between docked and undocked dimensions. We take the liberty of applying
// the resolution scaling factor here.
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
- static_cast<u32>(Settings::values.resolution_factor));
+ static_cast<u32>(Settings::values.resolution_factor.GetValue()));
}
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
@@ -1052,8 +1090,8 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called");
DisplayInfo display_info;
- display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
- display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
+ display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
+ display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -1074,6 +1112,7 @@ private:
const auto display_id = nv_flinger->OpenDisplay(display_name);
if (!display_id) {
+ LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
@@ -1081,6 +1120,7 @@ private:
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id);
if (!buffer_queue_id) {
+ LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
@@ -1116,6 +1156,7 @@ private:
const auto layer_id = nv_flinger->CreateLayer(display_id);
if (!layer_id) {
+ LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
@@ -1123,6 +1164,7 @@ private:
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id);
if (!buffer_queue_id) {
+ LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
@@ -1153,6 +1195,7 @@ private:
const auto vsync_event = nv_flinger->FindVsyncEvent(display_id);
if (!vsync_event) {
+ LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
@@ -1180,6 +1223,23 @@ private:
}
}
+ void GetIndirectLayerImageRequiredMemoryInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto width = rp.Pop<u64>();
+ const auto height = rp.Pop<u64>();
+ LOG_DEBUG(Service_VI, "called width={}, height={}", width, height);
+
+ constexpr std::size_t base_size = 0x20000;
+ constexpr std::size_t alignment = 0x1000;
+ const auto texture_size = width * height * 4;
+ const auto out_size = (texture_size + base_size - 1) / base_size * base_size;
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(out_size);
+ rb.Push(alignment);
+ }
+
static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
switch (mode) {
case NintendoScaleMode::None:
@@ -1193,6 +1253,7 @@ private:
case NintendoScaleMode::PreserveAspectRatio:
return MakeResult(ConvertedScaleMode::PreserveAspectRatio);
default:
+ LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode);
return ERR_OPERATION_FAILED;
}
}
@@ -1223,7 +1284,8 @@ IApplicationDisplayService::IApplicationDisplayService(
{2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
{2450, nullptr, "GetIndirectLayerImageMap"},
{2451, nullptr, "GetIndirectLayerImageCropMap"},
- {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"},
+ {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
+ "GetIndirectLayerImageRequiredMemoryInfo"},
{5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
{5203, nullptr, "GetDisplayVsyncEventForDebug"},
};
@@ -1249,6 +1311,7 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
const auto policy = rp.PopEnum<Policy>();
if (!IsValidServiceAccess(permission, policy)) {
+ LOG_ERROR(Service_VI, "Permission denied for policy {}", static_cast<u32>(policy));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_PERMISSION_DENIED);
return;
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 9d5ceb608..6b7329345 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -12,6 +12,7 @@ VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{0, &VI_U::GetDisplayService, "GetDisplayService"},
+ {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp
index 2654594c1..0260d7dcf 100644
--- a/src/core/hle/service/wlan/wlan.cpp
+++ b/src/core/hle/service/wlan/wlan.cpp
@@ -15,34 +15,37 @@ public:
explicit WLANInfra() : ServiceFramework{"wlan:inf"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
+ {0, nullptr, "OpenMode"},
+ {1, nullptr, "CloseMode"},
{2, nullptr, "GetMacAddress"},
{3, nullptr, "StartScan"},
{4, nullptr, "StopScan"},
{5, nullptr, "Connect"},
{6, nullptr, "CancelConnect"},
{7, nullptr, "Disconnect"},
- {8, nullptr, "Unknown3"},
- {9, nullptr, "Unknown4"},
+ {8, nullptr, "GetConnectionEvent"},
+ {9, nullptr, "GetConnectionStatus"},
{10, nullptr, "GetState"},
{11, nullptr, "GetScanResult"},
{12, nullptr, "GetRssi"},
{13, nullptr, "ChangeRxAntenna"},
- {14, nullptr, "Unknown5"},
- {15, nullptr, "Unknown6"},
+ {14, nullptr, "GetFwVersion"},
+ {15, nullptr, "RequestSleep"},
{16, nullptr, "RequestWakeUp"},
{17, nullptr, "RequestIfUpDown"},
- {18, nullptr, "Unknown7"},
- {19, nullptr, "Unknown8"},
- {20, nullptr, "Unknown9"},
- {21, nullptr, "Unknown10"},
- {22, nullptr, "Unknown11"},
- {23, nullptr, "Unknown12"},
- {24, nullptr, "Unknown13"},
- {25, nullptr, "Unknown14"},
- {26, nullptr, "Unknown15"},
- {27, nullptr, "Unknown16"},
+ {18, nullptr, "Unknown18"},
+ {19, nullptr, "Unknown19"},
+ {20, nullptr, "Unknown20"},
+ {21, nullptr, "Unknown21"},
+ {22, nullptr, "Unknown22"},
+ {23, nullptr, "Unknown23"},
+ {24, nullptr, "Unknown24"},
+ {25, nullptr, "Unknown25"},
+ {26, nullptr, "Unknown26"},
+ {27, nullptr, "Unknown27"},
+ {28, nullptr, "Unknown28"},
+ {29, nullptr, "Unknown29"},
+ {30, nullptr, "Unknown30"},
};
// clang-format on
@@ -55,12 +58,12 @@ public:
explicit WLANLocal() : ServiceFramework{"wlan:lcl"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
- {4, nullptr, "Unknown5"},
- {5, nullptr, "Unknown6"},
+ {0, nullptr, "Unknown0"},
+ {1, nullptr, "Unknown1"},
+ {2, nullptr, "Unknown2"},
+ {3, nullptr, "Unknown3"},
+ {4, nullptr, "Unknown4"},
+ {5, nullptr, "Unknown5"},
{6, nullptr, "GetMacAddress"},
{7, nullptr, "CreateBss"},
{8, nullptr, "DestroyBss"},
@@ -72,38 +75,42 @@ public:
{14, nullptr, "CancelJoin"},
{15, nullptr, "Disconnect"},
{16, nullptr, "SetBeaconLostCount"},
- {17, nullptr, "Unknown7"},
- {18, nullptr, "Unknown8"},
- {19, nullptr, "Unknown9"},
+ {17, nullptr, "Unknown17"},
+ {18, nullptr, "Unknown18"},
+ {19, nullptr, "Unknown19"},
{20, nullptr, "GetBssIndicationEvent"},
{21, nullptr, "GetBssIndicationInfo"},
{22, nullptr, "GetState"},
{23, nullptr, "GetAllowedChannels"},
{24, nullptr, "AddIe"},
{25, nullptr, "DeleteIe"},
- {26, nullptr, "Unknown10"},
- {27, nullptr, "Unknown11"},
+ {26, nullptr, "Unknown26"},
+ {27, nullptr, "Unknown27"},
{28, nullptr, "CreateRxEntry"},
{29, nullptr, "DeleteRxEntry"},
- {30, nullptr, "Unknown12"},
- {31, nullptr, "Unknown13"},
+ {30, nullptr, "Unknown30"},
+ {31, nullptr, "Unknown31"},
{32, nullptr, "AddMatchingDataToRxEntry"},
{33, nullptr, "RemoveMatchingDataFromRxEntry"},
{34, nullptr, "GetScanResult"},
- {35, nullptr, "Unknown14"},
+ {35, nullptr, "Unknown35"},
{36, nullptr, "SetActionFrameWithBeacon"},
{37, nullptr, "CancelActionFrameWithBeacon"},
{38, nullptr, "CreateRxEntryForActionFrame"},
{39, nullptr, "DeleteRxEntryForActionFrame"},
- {40, nullptr, "Unknown15"},
- {41, nullptr, "Unknown16"},
+ {40, nullptr, "Unknown40"},
+ {41, nullptr, "Unknown41"},
{42, nullptr, "CancelGetActionFrame"},
{43, nullptr, "GetRssi"},
- {44, nullptr, "Unknown17"},
- {45, nullptr, "Unknown18"},
- {46, nullptr, "Unknown19"},
- {47, nullptr, "Unknown20"},
- {48, nullptr, "Unknown21"},
+ {44, nullptr, "Unknown44"},
+ {45, nullptr, "Unknown45"},
+ {46, nullptr, "Unknown46"},
+ {47, nullptr, "Unknown47"},
+ {48, nullptr, "Unknown48"},
+ {49, nullptr, "Unknown49"},
+ {50, nullptr, "Unknown50"},
+ {51, nullptr, "Unknown51"},
+ {52, nullptr, "Unknown52"},
};
// clang-format on
@@ -142,18 +149,19 @@ public:
explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
- {4, nullptr, "Unknown5"},
- {5, nullptr, "Unknown6"},
+ {0, nullptr, "Unknown0"},
+ {1, nullptr, "Unknown1"},
+ {2, nullptr, "Unknown2"},
+ {3, nullptr, "Unknown3"},
+ {4, nullptr, "Unknown4"},
+ {5, nullptr, "Unknown5"},
{6, nullptr, "GetMacAddress"},
{7, nullptr, "SwitchTsfTimerFunction"},
- {8, nullptr, "Unknown7"},
- {9, nullptr, "Unknown8"},
- {10, nullptr, "Unknown9"},
- {11, nullptr, "Unknown10"},
+ {8, nullptr, "Unknown8"},
+ {9, nullptr, "Unknown9"},
+ {10, nullptr, "Unknown10"},
+ {11, nullptr, "Unknown11"},
+ {12, nullptr, "Unknown12"},
};
// clang-format on