summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/ipc.h8
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp317
-rw-r--r--src/core/hle/kernel/address_arbiter.h91
-rw-r--r--src/core/hle/kernel/client_port.cpp7
-rw-r--r--src/core/hle/kernel/client_port.h2
-rw-r--r--src/core/hle/kernel/client_session.cpp19
-rw-r--r--src/core/hle/kernel/client_session.h14
-rw-r--r--src/core/hle/kernel/errors.h40
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp4
-rw-r--r--src/core/hle/kernel/global_scheduler_context.h19
-rw-r--r--src/core/hle/kernel/handle_table.cpp16
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp34
-rw-r--r--src/core/hle/kernel/hle_ipc.h25
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp341
-rw-r--r--src/core/hle/kernel/k_address_arbiter.h70
-rw-r--r--src/core/hle/kernel/k_affinity_mask.h2
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp345
-rw-r--r--src/core/hle/kernel/k_condition_variable.h59
-rw-r--r--src/core/hle/kernel/k_event.cpp32
-rw-r--r--src/core/hle/kernel/k_event.h57
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.h57
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp130
-rw-r--r--src/core/hle/kernel/k_light_lock.h41
-rw-r--r--src/core/hle/kernel/k_priority_queue.h26
-rw-r--r--src/core/hle/kernel/k_readable_event.cpp56
-rw-r--r--src/core/hle/kernel/k_readable_event.h51
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp152
-rw-r--r--src/core/hle/kernel/k_resource_limit.h81
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp290
-rw-r--r--src/core/hle/kernel/k_scheduler.h50
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h36
-rw-r--r--src/core/hle/kernel/k_scoped_resource_reservation.h67
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h18
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp171
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h59
-rw-r--r--src/core/hle/kernel/k_thread.cpp1048
-rw-r--r--src/core/hle/kernel/k_thread.h768
-rw-r--r--src/core/hle/kernel/k_thread_queue.h81
-rw-r--r--src/core/hle/kernel/k_writable_event.cpp27
-rw-r--r--src/core/hle/kernel/k_writable_event.h44
-rw-r--r--src/core/hle/kernel/kernel.cpp134
-rw-r--r--src/core/hle/kernel/kernel.h29
-rw-r--r--src/core/hle/kernel/memory/address_space_info.cpp2
-rw-r--r--src/core/hle/kernel/memory/memory_layout.h19
-rw-r--r--src/core/hle/kernel/memory/memory_manager.cpp6
-rw-r--r--src/core/hle/kernel/memory/page_heap.h4
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp87
-rw-r--r--src/core/hle/kernel/mutex.cpp170
-rw-r--r--src/core/hle/kernel/mutex.h42
-rw-r--r--src/core/hle/kernel/object.cpp6
-rw-r--r--src/core/hle/kernel/object.h14
-rw-r--r--src/core/hle/kernel/process.cpp210
-rw-r--r--src/core/hle/kernel/process.h156
-rw-r--r--src/core/hle/kernel/process_capability.cpp38
-rw-r--r--src/core/hle/kernel/readable_event.cpp54
-rw-r--r--src/core/hle/kernel/readable_event.h57
-rw-r--r--src/core/hle/kernel/resource_limit.cpp73
-rw-r--r--src/core/hle/kernel/resource_limit.h104
-rw-r--r--src/core/hle/kernel/server_port.cpp20
-rw-r--r--src/core/hle/kernel/server_port.h9
-rw-r--r--src/core/hle/kernel/server_session.cpp31
-rw-r--r--src/core/hle/kernel/server_session.h24
-rw-r--r--src/core/hle/kernel/session.cpp22
-rw-r--r--src/core/hle/kernel/session.h8
-rw-r--r--src/core/hle/kernel/shared_memory.cpp11
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/svc.cpp1262
-rw-r--r--src/core/hle/kernel/svc_common.h14
-rw-r--r--src/core/hle/kernel/svc_results.h41
-rw-r--r--src/core/hle/kernel/svc_types.h30
-rw-r--r--src/core/hle/kernel/svc_wrap.h103
-rw-r--r--src/core/hle/kernel/synchronization.cpp116
-rw-r--r--src/core/hle/kernel/synchronization.h44
-rw-r--r--src/core/hle/kernel/synchronization_object.cpp49
-rw-r--r--src/core/hle/kernel/synchronization_object.h77
-rw-r--r--src/core/hle/kernel/thread.cpp478
-rw-r--r--src/core/hle/kernel/thread.h731
-rw-r--r--src/core/hle/kernel/time_manager.cpp47
-rw-r--r--src/core/hle/kernel/time_manager.h10
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp2
-rw-r--r--src/core/hle/kernel/transfer_memory.h2
-rw-r--r--src/core/hle/kernel/writable_event.cpp45
-rw-r--r--src/core/hle/kernel/writable_event.h59
-rw-r--r--src/core/hle/service/acc/acc.cpp63
-rw-r--r--src/core/hle/service/acc/acc.h5
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp20
-rw-r--r--src/core/hle/service/acc/profile_manager.h18
-rw-r--r--src/core/hle/service/am/am.cpp133
-rw-r--r--src/core/hle/service/am/am.h26
-rw-r--r--src/core/hle/service/am/applets/applets.cpp40
-rw-r--r--src/core/hle/service/am/applets/applets.h18
-rw-r--r--src/core/hle/service/am/applets/controller.cpp5
-rw-r--r--src/core/hle/service/am/applets/error.cpp10
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp4
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp20
-rw-r--r--src/core/hle/service/aoc/aoc_u.h4
-rw-r--r--src/core/hle/service/apm/interface.cpp10
-rw-r--r--src/core/hle/service/apm/interface.h1
-rw-r--r--src/core/hle/service/audio/audout_u.cpp27
-rw-r--r--src/core/hle/service/audio/audren_u.cpp43
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp16
-rw-r--r--src/core/hle/service/bcat/backend/backend.h10
-rw-r--r--src/core/hle/service/bcat/module.cpp8
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp11
-rw-r--r--src/core/hle/service/btm/btm.cpp34
-rw-r--r--src/core/hle/service/friend/friend.cpp14
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h21
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h26
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp315
-rw-r--r--src/core/hle/service/hid/controllers/npad.h247
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp127
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h32
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h70
-rw-r--r--src/core/hle/service/hid/hid.cpp270
-rw-r--r--src/core/hle/service/hid/hid.h13
-rw-r--r--src/core/hle/service/lbl/lbl.cpp283
-rw-r--r--src/core/hle/service/ldn/errors.h13
-rw-r--r--src/core/hle/service/ldn/ldn.cpp36
-rw-r--r--src/core/hle/service/ldr/ldr.cpp6
-rw-r--r--src/core/hle/service/lm/lm.cpp321
-rw-r--r--src/core/hle/service/lm/manager.cpp134
-rw-r--r--src/core/hle/service/lm/manager.h106
-rw-r--r--src/core/hle/service/mii/manager.cpp51
-rw-r--r--src/core/hle/service/mii/manager.h112
-rw-r--r--src/core/hle/service/mii/raw_data.cpp3848
-rw-r--r--src/core/hle/service/mii/raw_data.h27
-rw-r--r--src/core/hle/service/nfp/nfp.cpp41
-rw-r--r--src/core/hle/service/nfp/nfp.h11
-rw-r--r--src/core/hle/service/nifm/nifm.cpp180
-rw-r--r--src/core/hle/service/nim/nim.cpp18
-rw-r--r--src/core/hle/service/ns/pl_u.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h4
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp6
-rw-r--r--src/core/hle/service/nvdrv/interface.h2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp24
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h12
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp57
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h18
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp8
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h6
-rw-r--r--src/core/hle/service/olsc/olsc.cpp13
-rw-r--r--src/core/hle/service/pctl/module.cpp57
-rw-r--r--src/core/hle/service/prepo/prepo.cpp98
-rw-r--r--src/core/hle/service/ptm/psm.cpp115
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/sm/sm.cpp3
-rw-r--r--src/core/hle/service/sockets/bsd.cpp41
-rw-r--r--src/core/hle/service/sockets/bsd.h2
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp1
-rw-r--r--src/core/hle/service/time/clock_types.h26
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp8
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h7
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp4
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h6
-rw-r--r--src/core/hle/service/time/time.cpp2
-rw-r--r--src/core/hle/service/time/time.h2
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h2
-rw-r--r--src/core/hle/service/time/time_zone_types.h22
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp14
-rw-r--r--src/core/hle/service/vi/display/vi_display.h9
-rw-r--r--src/core/hle/service/vi/vi.cpp6
172 files changed, 9568 insertions, 7173 deletions
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 7ce313190..55b1716e4 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -146,7 +146,7 @@ static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorre
struct DataPayloadHeader {
u32_le magic;
- INSERT_PADDING_WORDS(1);
+ INSERT_PADDING_WORDS_NOINIT(1);
};
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
@@ -160,7 +160,7 @@ struct DomainMessageHeader {
// Used when responding to an IPC request, Server -> Client.
struct {
u32_le num_objects;
- INSERT_UNION_PADDING_WORDS(3);
+ INSERT_PADDING_WORDS_NOINIT(3);
};
// Used when performing an IPC request, Client -> Server.
@@ -171,10 +171,10 @@ struct DomainMessageHeader {
BitField<16, 16, u32> size;
};
u32_le object_id;
- INSERT_UNION_PADDING_WORDS(2);
+ INSERT_PADDING_WORDS_NOINIT(2);
};
- std::array<u32, 4> raw{};
+ std::array<u32, 4> raw;
};
};
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
deleted file mode 100644
index 20ffa7d47..000000000
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ /dev/null
@@ -1,317 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <vector>
-
-#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/k_scheduler.h"
-#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/time_manager.h"
-#include "core/hle/result.h"
-#include "core/memory.h"
-
-namespace Kernel {
-
-// Wake up num_to_wake (or all) threads in a vector.
-void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
- s32 num_to_wake) {
- // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
- // them all.
- std::size_t last = waiting_threads.size();
- if (num_to_wake > 0) {
- last = std::min(last, static_cast<std::size_t>(num_to_wake));
- }
-
- // Signal the waiting threads.
- for (std::size_t i = 0; i < last; i++) {
- waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
- RemoveThread(waiting_threads[i]);
- waiting_threads[i]->WaitForArbitration(false);
- waiting_threads[i]->ResumeFromWait();
- }
-}
-
-AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
-AddressArbiter::~AddressArbiter() = default;
-
-ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
- s32 num_to_wake) {
- switch (type) {
- case SignalType::Signal:
- return SignalToAddressOnly(address, num_to_wake);
- case SignalType::IncrementAndSignalIfEqual:
- return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
- case SignalType::ModifyByWaitingCountAndSignalIfEqual:
- return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
- default:
- return ERR_INVALID_ENUM_VALUE;
- }
-}
-
-ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
- KScopedSchedulerLock lock(system.Kernel());
- const std::vector<std::shared_ptr<Thread>> waiting_threads =
- GetThreadsWaitingOnAddress(address);
- WakeThreads(waiting_threads, num_to_wake);
- return RESULT_SUCCESS;
-}
-
-ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
- s32 num_to_wake) {
- KScopedSchedulerLock lock(system.Kernel());
- auto& memory = system.Memory();
-
- // Ensure that we can write to the address.
- if (!memory.IsValidVirtualAddress(address)) {
- return ERR_INVALID_ADDRESS_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));
-
- return SignalToAddressOnly(address, num_to_wake);
-}
-
-ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
- s32 num_to_wake) {
- KScopedSchedulerLock lock(system.Kernel());
- auto& memory = system.Memory();
-
- // Ensure that we can write to the address.
- if (!memory.IsValidVirtualAddress(address)) {
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- // Get threads waiting on the address.
- const std::vector<std::shared_ptr<Thread>> waiting_threads =
- GetThreadsWaitingOnAddress(address);
-
- const std::size_t current_core = system.CurrentCoreIndex();
- auto& monitor = system.Monitor();
- s32 updated_value;
- do {
- updated_value = monitor.ExclusiveRead32(current_core, address);
-
- if (updated_value != value) {
- return ERR_INVALID_STATE;
- }
- // 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 {
- 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));
-
- WakeThreads(waiting_threads, num_to_wake);
- return RESULT_SUCCESS;
-}
-
-ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
- s64 timeout_ns) {
- switch (type) {
- case ArbitrationType::WaitIfLessThan:
- return WaitForAddressIfLessThan(address, value, timeout_ns, false);
- case ArbitrationType::DecrementAndWaitIfLessThan:
- return WaitForAddressIfLessThan(address, value, timeout_ns, true);
- case ArbitrationType::WaitIfEqual:
- return WaitForAddressIfEqual(address, value, timeout_ns);
- default:
- return ERR_INVALID_ENUM_VALUE;
- }
-}
-
-ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
- bool should_decrement) {
- auto& memory = system.Memory();
- auto& kernel = system.Kernel();
- Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
-
- Handle event_handle = InvalidHandle;
- {
- KScopedSchedulerLockAndSleep 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;
- }
-
- current_thread->SetArbiterWaitAddress(address);
- InsertThread(SharedFrom(current_thread));
- current_thread->SetStatus(ThreadStatus::WaitArb);
- current_thread->WaitForArbitration(true);
- }
-
- if (event_handle != InvalidHandle) {
- auto& time_manager = kernel.TimeManager();
- time_manager.UnscheduleTimeEvent(event_handle);
- }
-
- {
- KScopedSchedulerLock lock(kernel);
- if (current_thread->IsWaitingForArbitration()) {
- RemoveThread(SharedFrom(current_thread));
- current_thread->WaitForArbitration(false);
- }
- }
-
- return current_thread->GetSignalingResult();
-}
-
-ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
- auto& memory = system.Memory();
- auto& kernel = system.Kernel();
- Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
-
- Handle event_handle = InvalidHandle;
- {
- KScopedSchedulerLockAndSleep 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;
- }
-
- // 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);
- }
-
- if (event_handle != InvalidHandle) {
- auto& time_manager = kernel.TimeManager();
- time_manager.UnscheduleTimeEvent(event_handle);
- }
-
- {
- KScopedSchedulerLock lock(kernel);
- if (current_thread->IsWaitingForArbitration()) {
- RemoveThread(SharedFrom(current_thread));
- current_thread->WaitForArbitration(false);
- }
- }
-
- return current_thread->GetSignalingResult();
-}
-
-void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
- const VAddr arb_addr = thread->GetArbiterWaitAddress();
- std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
-
- const auto iter =
- std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) {
- return entry->GetPriority() >= thread->GetPriority();
- });
-
- if (iter == thread_list.cend()) {
- thread_list.push_back(std::move(thread));
- } else {
- thread_list.insert(iter, std::move(thread));
- }
-}
-
-void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
- const VAddr arb_addr = thread->GetArbiterWaitAddress();
- std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
-
- const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(),
- [&thread](const auto& entry) { return thread == entry; });
-
- if (iter != thread_list.cend()) {
- thread_list.erase(iter);
- }
-}
-
-std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
- VAddr address) const {
- const auto iter = arb_threads.find(address);
- if (iter == arb_threads.cend()) {
- return {};
- }
-
- const std::list<std::shared_ptr<Thread>>& thread_list = iter->second;
- return {thread_list.cbegin(), thread_list.cend()};
-}
-} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
deleted file mode 100644
index b91edc67d..000000000
--- a/src/core/hle/kernel/address_arbiter.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <list>
-#include <memory>
-#include <unordered_map>
-#include <vector>
-
-#include "common/common_types.h"
-
-union ResultCode;
-
-namespace Core {
-class System;
-}
-
-namespace Kernel {
-
-class Thread;
-
-class AddressArbiter {
-public:
- enum class ArbitrationType {
- WaitIfLessThan = 0,
- DecrementAndWaitIfLessThan = 1,
- WaitIfEqual = 2,
- };
-
- enum class SignalType {
- Signal = 0,
- IncrementAndSignalIfEqual = 1,
- ModifyByWaitingCountAndSignalIfEqual = 2,
- };
-
- explicit AddressArbiter(Core::System& system);
- ~AddressArbiter();
-
- AddressArbiter(const AddressArbiter&) = delete;
- AddressArbiter& operator=(const AddressArbiter&) = delete;
-
- AddressArbiter(AddressArbiter&&) = default;
- AddressArbiter& operator=(AddressArbiter&&) = delete;
-
- /// Signals an address being waited on with a particular signaling type.
- ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
-
- /// Waits on an address with a particular arbitration type.
- ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
-
-private:
- /// Signals an address being waited on.
- ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
-
- /// Signals an address being waited on and increments its value if equal to the value argument.
- ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
-
- /// Signals an address being waited on and modifies its value based on waiting thread count if
- /// equal to the value argument.
- ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
- s32 num_to_wake);
-
- /// Waits on an address if the value passed is less than the argument value,
- /// optionally decrementing.
- ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
- bool should_decrement);
-
- /// Waits on an address if the value passed is equal to the argument value.
- ResultCode WaitForAddressIfEqual(VAddr address, s32 value, 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);
-
- /// Insert a thread into the address arbiter container
- void InsertThread(std::shared_ptr<Thread> thread);
-
- /// Removes a thread from the address arbiter container
- void RemoveThread(std::shared_ptr<Thread> thread);
-
- // Gets the threads waiting on an address.
- std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
-
- /// List of threads waiting for a address arbiter
- std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
-
- Core::System& system;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 8aff2227a..0b6957e31 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -4,11 +4,11 @@
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/session.h"
+#include "core/hle/kernel/svc_results.h"
namespace Kernel {
@@ -21,7 +21,7 @@ std::shared_ptr<ServerPort> ClientPort::GetServerPort() const {
ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
if (active_sessions >= max_sessions) {
- return ERR_MAX_CONNECTIONS_REACHED;
+ return ResultMaxConnectionsReached;
}
active_sessions++;
@@ -33,9 +33,6 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
server_port->AppendPendingSession(std::move(server));
}
- // Wake the threads waiting on the ServerPort
- server_port->Signal();
-
return MakeResult(std::move(client));
}
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index 9762bbf0d..77559ebf9 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -51,6 +51,8 @@ public:
*/
void ConnectionClosed();
+ void Finalize() override {}
+
private:
std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index be9eba519..e230f365a 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -3,16 +3,16 @@
// Refer to the license.txt file included.
#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/svc_results.h"
#include "core/hle/result.h"
namespace Kernel {
-ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
+ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
ClientSession::~ClientSession() {
// This destructor will be called automatically when the last ClientSession handle is closed by
@@ -22,15 +22,6 @@ ClientSession::~ClientSession() {
}
}
-bool ClientSession::ShouldWait(const Thread* thread) const {
- UNIMPLEMENTED();
- return {};
-}
-
-void ClientSession::Acquire(Thread* thread) {
- UNIMPLEMENTED();
-}
-
bool ClientSession::IsSignaled() const {
UNIMPLEMENTED();
return true;
@@ -47,12 +38,12 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
return MakeResult(std::move(client_session));
}
-ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
+ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> 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;
+ return ResultSessionClosedByRemote;
}
// Signal the server session that new data is available
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index e5e0690c2..85aafeaf4 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -7,7 +7,7 @@
#include <memory>
#include <string>
-#include "core/hle/kernel/synchronization_object.h"
+#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/result.h"
union ResultCode;
@@ -24,9 +24,9 @@ namespace Kernel {
class KernelCore;
class Session;
-class Thread;
+class KThread;
-class ClientSession final : public SynchronizationObject {
+class ClientSession final : public KSynchronizationObject {
public:
explicit ClientSession(KernelCore& kernel);
~ClientSession() override;
@@ -46,15 +46,13 @@ public:
return HANDLE_TYPE;
}
- ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
+ ResultCode SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing);
- bool ShouldWait(const Thread* thread) const override;
-
- void Acquire(Thread* thread) override;
-
bool IsSignaled() const override;
+ void Finalize() override {}
+
private:
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
std::shared_ptr<Session> parent,
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
deleted file mode 100644
index d4e5d88cf..000000000
--- a/src/core/hle/kernel/errors.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/hle/result.h"
-
-namespace Kernel {
-
-// Confirmed Switch kernel error codes
-
-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};
-constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
-constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
-constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
-constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
-constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
-constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
-constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
-constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
-constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
-constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
-constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
-constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
-constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
-constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
-constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
-constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
-constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index a133e8ed0..c6838649f 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
-void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) {
+void GlobalSchedulerContext::AddThread(std::shared_ptr<KThread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.push_back(std::move(thread));
}
-void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) {
+void GlobalSchedulerContext::RemoveThread(std::shared_ptr<KThread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 5c7b89290..11592843e 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -12,7 +12,8 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/svc_types.h"
namespace Kernel {
@@ -20,8 +21,12 @@ class KernelCore;
class SchedulerLock;
using KSchedulerPriorityQueue =
- KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>;
-constexpr s32 HighestCoreMigrationAllowedPriority = 2;
+ KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
+ Svc::HighestThreadPriority>;
+
+static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
+static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
+static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
class GlobalSchedulerContext final {
friend class KScheduler;
@@ -33,13 +38,13 @@ public:
~GlobalSchedulerContext();
/// Adds a new thread to the scheduler
- void AddThread(std::shared_ptr<Thread> thread);
+ void AddThread(std::shared_ptr<KThread> thread);
/// Removes a thread from the scheduler
- void RemoveThread(std::shared_ptr<Thread> thread);
+ void RemoveThread(std::shared_ptr<KThread> thread);
/// Returns a list of all threads managed by the scheduler
- [[nodiscard]] const std::vector<std::shared_ptr<Thread>>& GetThreadList() const {
+ [[nodiscard]] const std::vector<std::shared_ptr<KThread>>& GetThreadList() const {
return thread_list;
}
@@ -74,7 +79,7 @@ private:
LockType scheduler_lock;
/// Lists all thread ids that aren't deleted/etc.
- std::vector<std::shared_ptr<Thread>> thread_list;
+ std::vector<std::shared_ptr<KThread>> thread_list;
Common::SpinLock global_list_guard{};
};
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index 40988b0fd..f96d34078 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -6,12 +6,12 @@
#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"
#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
@@ -33,7 +33,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;
+ return ResultOutOfMemory;
}
// Values less than or equal to zero indicate to use the maximum allowable
@@ -53,7 +53,7 @@ ResultVal<Handle> HandleTable::Create(std::shared_ptr<Object> obj) {
const u16 slot = next_free_slot;
if (slot >= table_size) {
LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
- return ERR_HANDLE_TABLE_FULL;
+ return ResultHandleTableFull;
}
next_free_slot = generations[slot];
@@ -76,7 +76,7 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
std::shared_ptr<Object> object = GetGeneric(handle);
if (object == nullptr) {
LOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
return Create(std::move(object));
}
@@ -84,11 +84,15 @@ 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;
+ return ResultInvalidHandle;
}
const u16 slot = GetSlot(handle);
+ if (objects[slot].use_count() == 1) {
+ objects[slot]->Finalize();
+ }
+
objects[slot] = nullptr;
generations[slot] = next_free_slot;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 83decf6cf..161d9f782 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -14,19 +14,19 @@
#include "common/common_types.h"
#include "common/logging/log.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/k_readable_event.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_writable_event.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/server_session.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
namespace Kernel {
@@ -48,7 +48,7 @@ void SessionRequestHandler::ClientDisconnected(
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> server_session,
- std::shared_ptr<Thread> thread)
+ std::shared_ptr<KThread> thread)
: server_session(std::move(server_session)),
thread(std::move(thread)), kernel{kernel}, memory{memory} {
cmd_buf[0] = 0;
@@ -182,7 +182,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
return RESULT_SUCCESS;
}
-ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
+ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) {
auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
@@ -338,6 +338,28 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
return 0;
}
+bool HLERequestContext::CanReadBuffer(std::size_t buffer_index) const {
+ const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
+ BufferDescriptorA()[buffer_index].Size()};
+
+ if (is_buffer_a) {
+ return BufferDescriptorA().size() > buffer_index;
+ } else {
+ return BufferDescriptorX().size() > buffer_index;
+ }
+}
+
+bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
+ const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
+ BufferDescriptorB()[buffer_index].Size()};
+
+ if (is_buffer_b) {
+ return BufferDescriptorB().size() > buffer_index;
+ } else {
+ return BufferDescriptorC().size() > buffer_index;
+ }
+}
+
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index b112e1ebd..9a769781b 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -40,9 +40,9 @@ class HLERequestContext;
class KernelCore;
class Process;
class ServerSession;
-class Thread;
-class ReadableEvent;
-class WritableEvent;
+class KThread;
+class KReadableEvent;
+class KWritableEvent;
enum class ThreadWakeupReason;
@@ -110,7 +110,7 @@ class HLERequestContext {
public:
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> session,
- std::shared_ptr<Thread> thread);
+ std::shared_ptr<KThread> thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -126,15 +126,12 @@ public:
return server_session;
}
- using WakeupCallback = std::function<void(
- std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
-
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
- ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
+ ResultCode WriteToOutgoingCommandBuffer(KThread& thread);
u32_le GetCommand() const {
return command;
@@ -207,6 +204,12 @@ public:
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
+ /// Helper function to test whether the input buffer at buffer_index can be read
+ bool CanReadBuffer(std::size_t buffer_index = 0) const;
+
+ /// Helper function to test whether the output buffer at buffer_index can be written
+ bool CanWriteBuffer(std::size_t buffer_index = 0) const;
+
template <typename T>
std::shared_ptr<T> GetCopyObject(std::size_t index) {
return DynamicObjectCast<T>(copy_objects.at(index));
@@ -261,11 +264,11 @@ public:
std::string Description() const;
- Thread& GetThread() {
+ KThread& GetThread() {
return *thread;
}
- const Thread& GetThread() const {
+ const KThread& GetThread() const {
return *thread;
}
@@ -280,7 +283,7 @@ private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
std::shared_ptr<Kernel::ServerSession> server_session;
- std::shared_ptr<Thread> thread;
+ std::shared_ptr<KThread> thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
new file mode 100644
index 000000000..7018f56da
--- /dev/null
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -0,0 +1,341 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/arm/exclusive_monitor.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_address_arbiter.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/hle/kernel/time_manager.h"
+#include "core/memory.h"
+
+namespace Kernel {
+
+KAddressArbiter::KAddressArbiter(Core::System& system_)
+ : system{system_}, kernel{system.Kernel()} {}
+KAddressArbiter::~KAddressArbiter() = default;
+
+namespace {
+
+bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
+ *out = system.Memory().Read32(address);
+ return true;
+}
+
+bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
+ auto& monitor = system.Monitor();
+ const auto current_core = system.CurrentCoreIndex();
+
+ // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
+ // TODO(bunnei): We should call CanAccessAtomic(..) here.
+
+ // Load the value from the address.
+ const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
+
+ // Compare it to the desired one.
+ if (current_value < value) {
+ // If less than, we want to try to decrement.
+ const s32 decrement_value = current_value - 1;
+
+ // Decrement and try to store.
+ if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) {
+ // If we failed to store, try again.
+ DecrementIfLessThan(system, out, address, value);
+ }
+ } else {
+ // Otherwise, clear our exclusive hold and finish
+ monitor.ClearExclusive();
+ }
+
+ // We're done.
+ *out = current_value;
+ return true;
+}
+
+bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
+ auto& monitor = system.Monitor();
+ const auto current_core = system.CurrentCoreIndex();
+
+ // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
+ // TODO(bunnei): We should call CanAccessAtomic(..) here.
+
+ // Load the value from the address.
+ const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
+
+ // Compare it to the desired one.
+ if (current_value == value) {
+ // If equal, we want to try to write the new value.
+
+ // Try to store.
+ if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) {
+ // If we failed to store, try again.
+ UpdateIfEqual(system, out, address, value, new_value);
+ }
+ } else {
+ // Otherwise, clear our exclusive hold and finish.
+ monitor.ClearExclusive();
+ }
+
+ // We're done.
+ *out = current_value;
+ return true;
+}
+
+} // namespace
+
+ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
+ // Perform signaling.
+ s32 num_waiters{};
+ {
+ KScopedSchedulerLock sl(kernel);
+
+ auto it = thread_tree.nfind_light({addr, -1});
+ while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
+ (it->GetAddressArbiterKey() == addr)) {
+ KThread* target_thread = std::addressof(*it);
+ target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
+
+ ASSERT(target_thread->IsWaitingForAddressArbiter());
+ target_thread->Wakeup();
+
+ it = thread_tree.erase(it);
+ target_thread->ClearAddressArbiter();
+ ++num_waiters;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
+ // Perform signaling.
+ s32 num_waiters{};
+ {
+ KScopedSchedulerLock sl(kernel);
+
+ // Check the userspace value.
+ s32 user_value{};
+ if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) {
+ LOG_ERROR(Kernel, "Invalid current memory!");
+ return ResultInvalidCurrentMemory;
+ }
+ if (user_value != value) {
+ return ResultInvalidState;
+ }
+
+ auto it = thread_tree.nfind_light({addr, -1});
+ while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
+ (it->GetAddressArbiterKey() == addr)) {
+ KThread* target_thread = std::addressof(*it);
+ target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
+
+ ASSERT(target_thread->IsWaitingForAddressArbiter());
+ target_thread->Wakeup();
+
+ it = thread_tree.erase(it);
+ target_thread->ClearAddressArbiter();
+ ++num_waiters;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
+ // Perform signaling.
+ s32 num_waiters{};
+ {
+ [[maybe_unused]] const KScopedSchedulerLock sl(kernel);
+
+ auto it = thread_tree.nfind_light({addr, -1});
+ // Determine the updated value.
+ s32 new_value{};
+ if (count <= 0) {
+ if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
+ new_value = value - 2;
+ } else {
+ new_value = value + 1;
+ }
+ } else {
+ if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
+ auto tmp_it = it;
+ s32 tmp_num_waiters{};
+ while (++tmp_it != thread_tree.end() && tmp_it->GetAddressArbiterKey() == addr) {
+ if (tmp_num_waiters++ >= count) {
+ break;
+ }
+ }
+
+ if (tmp_num_waiters < count) {
+ new_value = value - 1;
+ } else {
+ new_value = value;
+ }
+ } else {
+ new_value = value + 1;
+ }
+ }
+
+ // Check the userspace value.
+ s32 user_value{};
+ bool succeeded{};
+ if (value != new_value) {
+ succeeded = UpdateIfEqual(system, &user_value, addr, value, new_value);
+ } else {
+ succeeded = ReadFromUser(system, &user_value, addr);
+ }
+
+ if (!succeeded) {
+ LOG_ERROR(Kernel, "Invalid current memory!");
+ return ResultInvalidCurrentMemory;
+ }
+ if (user_value != value) {
+ return ResultInvalidState;
+ }
+
+ while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
+ (it->GetAddressArbiterKey() == addr)) {
+ KThread* target_thread = std::addressof(*it);
+ target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
+
+ ASSERT(target_thread->IsWaitingForAddressArbiter());
+ target_thread->Wakeup();
+
+ it = thread_tree.erase(it);
+ target_thread->ClearAddressArbiter();
+ ++num_waiters;
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
+ // Prepare to wait.
+ KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+
+ {
+ KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
+
+ // Check that the thread isn't terminating.
+ if (cur_thread->IsTerminationRequested()) {
+ slp.CancelSleep();
+ return ResultTerminationRequested;
+ }
+
+ // Set the synced object.
+ cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
+
+ // Read the value from userspace.
+ s32 user_value{};
+ bool succeeded{};
+ if (decrement) {
+ succeeded = DecrementIfLessThan(system, &user_value, addr, value);
+ } else {
+ succeeded = ReadFromUser(system, &user_value, addr);
+ }
+
+ if (!succeeded) {
+ slp.CancelSleep();
+ return ResultInvalidCurrentMemory;
+ }
+
+ // Check that the value is less than the specified one.
+ if (user_value >= value) {
+ slp.CancelSleep();
+ return ResultInvalidState;
+ }
+
+ // Check that the timeout is non-zero.
+ if (timeout == 0) {
+ slp.CancelSleep();
+ return ResultTimedOut;
+ }
+
+ // Set the arbiter.
+ cur_thread->SetAddressArbiter(&thread_tree, addr);
+ thread_tree.insert(*cur_thread);
+ cur_thread->SetState(ThreadState::Waiting);
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
+ }
+
+ // Cancel the timer wait.
+ kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
+
+ // Remove from the address arbiter.
+ {
+ KScopedSchedulerLock sl(kernel);
+
+ if (cur_thread->IsWaitingForAddressArbiter()) {
+ thread_tree.erase(thread_tree.iterator_to(*cur_thread));
+ cur_thread->ClearAddressArbiter();
+ }
+ }
+
+ // Get the result.
+ KSynchronizationObject* dummy{};
+ return cur_thread->GetWaitResult(&dummy);
+}
+
+ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
+ // Prepare to wait.
+ KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+
+ {
+ KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
+
+ // Check that the thread isn't terminating.
+ if (cur_thread->IsTerminationRequested()) {
+ slp.CancelSleep();
+ return ResultTerminationRequested;
+ }
+
+ // Set the synced object.
+ cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
+
+ // Read the value from userspace.
+ s32 user_value{};
+ if (!ReadFromUser(system, &user_value, addr)) {
+ slp.CancelSleep();
+ return ResultInvalidCurrentMemory;
+ }
+
+ // Check that the value is equal.
+ if (value != user_value) {
+ slp.CancelSleep();
+ return ResultInvalidState;
+ }
+
+ // Check that the timeout is non-zero.
+ if (timeout == 0) {
+ slp.CancelSleep();
+ return ResultTimedOut;
+ }
+
+ // Set the arbiter.
+ cur_thread->SetAddressArbiter(&thread_tree, addr);
+ thread_tree.insert(*cur_thread);
+ cur_thread->SetState(ThreadState::Waiting);
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
+ }
+
+ // Cancel the timer wait.
+ kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
+
+ // Remove from the address arbiter.
+ {
+ KScopedSchedulerLock sl(kernel);
+
+ if (cur_thread->IsWaitingForAddressArbiter()) {
+ thread_tree.erase(thread_tree.iterator_to(*cur_thread));
+ cur_thread->ClearAddressArbiter();
+ }
+ }
+
+ // Get the result.
+ KSynchronizationObject* dummy{};
+ return cur_thread->GetWaitResult(&dummy);
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h
new file mode 100644
index 000000000..8d379b524
--- /dev/null
+++ b/src/core/hle/kernel/k_address_arbiter.h
@@ -0,0 +1,70 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/svc_types.h"
+
+union ResultCode;
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+
+class KernelCore;
+
+class KAddressArbiter {
+public:
+ using ThreadTree = KConditionVariable::ThreadTree;
+
+ explicit KAddressArbiter(Core::System& system_);
+ ~KAddressArbiter();
+
+ [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value,
+ s32 count) {
+ switch (type) {
+ case Svc::SignalType::Signal:
+ return Signal(addr, count);
+ case Svc::SignalType::SignalAndIncrementIfEqual:
+ return SignalAndIncrementIfEqual(addr, value, count);
+ case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
+ return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
+ }
+ UNREACHABLE();
+ return RESULT_UNKNOWN;
+ }
+
+ [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
+ s64 timeout) {
+ switch (type) {
+ case Svc::ArbitrationType::WaitIfLessThan:
+ return WaitIfLessThan(addr, value, false, timeout);
+ case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
+ return WaitIfLessThan(addr, value, true, timeout);
+ case Svc::ArbitrationType::WaitIfEqual:
+ return WaitIfEqual(addr, value, timeout);
+ }
+ UNREACHABLE();
+ return RESULT_UNKNOWN;
+ }
+
+private:
+ [[nodiscard]] ResultCode Signal(VAddr addr, s32 count);
+ [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
+ [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
+ [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
+ [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout);
+
+ ThreadTree thread_tree;
+
+ Core::System& system;
+ KernelCore& kernel;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_affinity_mask.h b/src/core/hle/kernel/k_affinity_mask.h
index dd73781cd..b906895fc 100644
--- a/src/core/hle/kernel/k_affinity_mask.h
+++ b/src/core/hle/kernel/k_affinity_mask.h
@@ -27,7 +27,7 @@ public:
}
[[nodiscard]] constexpr bool GetAffinity(s32 core) const {
- return this->mask & GetCoreBit(core);
+ return (this->mask & GetCoreBit(core)) != 0;
}
constexpr void SetAffinity(s32 core, bool set) {
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
new file mode 100644
index 000000000..170d8fa0d
--- /dev/null
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -0,0 +1,345 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <vector>
+
+#include "core/arm/exclusive_monitor.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/svc_common.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/memory.h"
+
+namespace Kernel {
+
+namespace {
+
+bool ReadFromUser(Core::System& system, u32* out, VAddr address) {
+ *out = system.Memory().Read32(address);
+ return true;
+}
+
+bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
+ system.Memory().Write32(address, *p);
+ return true;
+}
+
+bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
+ u32 new_orr_mask) {
+ auto& monitor = system.Monitor();
+ const auto current_core = system.CurrentCoreIndex();
+
+ // Load the value from the address.
+ const auto expected = monitor.ExclusiveRead32(current_core, address);
+
+ // Orr in the new mask.
+ u32 value = expected | new_orr_mask;
+
+ // If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
+ if (!expected) {
+ value = if_zero;
+ }
+
+ // Try to store.
+ if (!monitor.ExclusiveWrite32(current_core, address, value)) {
+ // If we failed to store, try again.
+ return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask);
+ }
+
+ // We're done.
+ *out = expected;
+ return true;
+}
+
+} // namespace
+
+KConditionVariable::KConditionVariable(Core::System& system_)
+ : system{system_}, kernel{system.Kernel()} {}
+
+KConditionVariable::~KConditionVariable() = default;
+
+ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
+ KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
+
+ // Signal the address.
+ {
+ KScopedSchedulerLock sl(kernel);
+
+ // Remove waiter thread.
+ s32 num_waiters{};
+ KThread* next_owner_thread =
+ owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
+
+ // Determine the next tag.
+ u32 next_value{};
+ if (next_owner_thread) {
+ next_value = next_owner_thread->GetAddressKeyValue();
+ if (num_waiters > 1) {
+ next_value |= Svc::HandleWaitMask;
+ }
+
+ next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
+ next_owner_thread->Wakeup();
+ }
+
+ // Write the value to userspace.
+ if (!WriteToUser(system, addr, std::addressof(next_value))) {
+ if (next_owner_thread) {
+ next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
+ }
+
+ return ResultInvalidCurrentMemory;
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
+ KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+
+ // Wait for the address.
+ {
+ std::shared_ptr<KThread> owner_thread;
+ ASSERT(!owner_thread);
+ {
+ KScopedSchedulerLock sl(kernel);
+ cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
+
+ // Check if the thread should terminate.
+ R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
+
+ {
+ // Read the tag from userspace.
+ u32 test_tag{};
+ R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
+ ResultInvalidCurrentMemory);
+
+ // If the tag isn't the handle (with wait mask), we're done.
+ R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
+
+ // Get the lock owner thread.
+ owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle);
+ R_UNLESS(owner_thread, ResultInvalidHandle);
+
+ // Update the lock.
+ cur_thread->SetAddressKey(addr, value);
+ owner_thread->AddWaiter(cur_thread);
+ cur_thread->SetState(ThreadState::Waiting);
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
+ cur_thread->SetMutexWaitAddressForDebugging(addr);
+ }
+ }
+ ASSERT(owner_thread);
+ }
+
+ // Remove the thread as a waiter from the lock owner.
+ {
+ KScopedSchedulerLock sl(kernel);
+ KThread* owner_thread = cur_thread->GetLockOwner();
+ if (owner_thread != nullptr) {
+ owner_thread->RemoveWaiter(cur_thread);
+ }
+ }
+
+ // Get the wait result.
+ KSynchronizationObject* dummy{};
+ return cur_thread->GetWaitResult(std::addressof(dummy));
+}
+
+KThread* KConditionVariable::SignalImpl(KThread* thread) {
+ // Check pre-conditions.
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Update the tag.
+ VAddr address = thread->GetAddressKey();
+ u32 own_tag = thread->GetAddressKeyValue();
+
+ u32 prev_tag{};
+ bool can_access{};
+ {
+ // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
+ // TODO(bunnei): We should call CanAccessAtomic(..) here.
+ can_access = true;
+ if (can_access) {
+ UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
+ Svc::HandleWaitMask);
+ }
+ }
+
+ KThread* thread_to_close = nullptr;
+ if (can_access) {
+ if (prev_tag == InvalidHandle) {
+ // If nobody held the lock previously, we're all good.
+ thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
+ thread->Wakeup();
+ } else {
+ // Get the previous owner.
+ auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(
+ prev_tag & ~Svc::HandleWaitMask);
+
+ if (owner_thread) {
+ // Add the thread as a waiter on the owner.
+ owner_thread->AddWaiter(thread);
+ thread_to_close = owner_thread.get();
+ } else {
+ // The lock was tagged with a thread that doesn't exist.
+ thread->SetSyncedObject(nullptr, ResultInvalidState);
+ thread->Wakeup();
+ }
+ }
+ } else {
+ // If the address wasn't accessible, note so.
+ thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
+ thread->Wakeup();
+ }
+
+ return thread_to_close;
+}
+
+void KConditionVariable::Signal(u64 cv_key, s32 count) {
+ // Prepare for signaling.
+ constexpr int MaxThreads = 16;
+
+ // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
+ // std::shared_ptr.
+ std::vector<std::shared_ptr<KThread>> thread_list;
+ std::array<KThread*, MaxThreads> thread_array;
+ s32 num_to_close{};
+
+ // Perform signaling.
+ s32 num_waiters{};
+ {
+ KScopedSchedulerLock sl(kernel);
+
+ auto it = thread_tree.nfind_light({cv_key, -1});
+ while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
+ (it->GetConditionVariableKey() == cv_key)) {
+ KThread* target_thread = std::addressof(*it);
+
+ if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
+ if (num_to_close < MaxThreads) {
+ thread_array[num_to_close++] = thread;
+ } else {
+ thread_list.push_back(SharedFrom(thread));
+ }
+ }
+
+ it = thread_tree.erase(it);
+ target_thread->ClearConditionVariable();
+ ++num_waiters;
+ }
+
+ // If we have no waiters, clear the has waiter flag.
+ if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
+ const u32 has_waiter_flag{};
+ WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
+ }
+ }
+
+ // Close threads in the array.
+ for (auto i = 0; i < num_to_close; ++i) {
+ thread_array[i]->Close();
+ }
+
+ // Close threads in the list.
+ for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
+ (*it)->Close();
+ }
+}
+
+ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
+ // Prepare to wait.
+ KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+
+ {
+ KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
+
+ // Set the synced object.
+ cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
+
+ // Check that the thread isn't terminating.
+ if (cur_thread->IsTerminationRequested()) {
+ slp.CancelSleep();
+ return ResultTerminationRequested;
+ }
+
+ // Update the value and process for the next owner.
+ {
+ // Remove waiter thread.
+ s32 num_waiters{};
+ KThread* next_owner_thread =
+ cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
+
+ // Update for the next owner thread.
+ u32 next_value{};
+ if (next_owner_thread != nullptr) {
+ // Get the next tag value.
+ next_value = next_owner_thread->GetAddressKeyValue();
+ if (num_waiters > 1) {
+ next_value |= Svc::HandleWaitMask;
+ }
+
+ // Wake up the next owner.
+ next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
+ next_owner_thread->Wakeup();
+ }
+
+ // Write to the cv key.
+ {
+ const u32 has_waiter_flag = 1;
+ WriteToUser(system, key, std::addressof(has_waiter_flag));
+ // TODO(bunnei): We should call DataMemoryBarrier(..) here.
+ }
+
+ // Write the value to userspace.
+ if (!WriteToUser(system, addr, std::addressof(next_value))) {
+ slp.CancelSleep();
+ return ResultInvalidCurrentMemory;
+ }
+ }
+
+ // Update condition variable tracking.
+ {
+ cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
+ thread_tree.insert(*cur_thread);
+ }
+
+ // If the timeout is non-zero, set the thread as waiting.
+ if (timeout != 0) {
+ cur_thread->SetState(ThreadState::Waiting);
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
+ cur_thread->SetMutexWaitAddressForDebugging(addr);
+ }
+ }
+
+ // Cancel the timer wait.
+ kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
+
+ // Remove from the condition variable.
+ {
+ KScopedSchedulerLock sl(kernel);
+
+ if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
+ owner->RemoveWaiter(cur_thread);
+ }
+
+ if (cur_thread->IsWaitingForConditionVariable()) {
+ thread_tree.erase(thread_tree.iterator_to(*cur_thread));
+ cur_thread->ClearConditionVariable();
+ }
+ }
+
+ // Get the result.
+ KSynchronizationObject* dummy{};
+ return cur_thread->GetWaitResult(std::addressof(dummy));
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
new file mode 100644
index 000000000..861dbd420
--- /dev/null
+++ b/src/core/hle/kernel/k_condition_variable.h
@@ -0,0 +1,59 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/assert.h"
+#include "common/common_types.h"
+
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+
+class KConditionVariable {
+public:
+ using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
+
+ explicit KConditionVariable(Core::System& system_);
+ ~KConditionVariable();
+
+ // Arbitration
+ [[nodiscard]] ResultCode SignalToAddress(VAddr addr);
+ [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value);
+
+ // Condition variable
+ void Signal(u64 cv_key, s32 count);
+ [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
+
+private:
+ [[nodiscard]] KThread* SignalImpl(KThread* thread);
+
+ ThreadTree thread_tree;
+
+ Core::System& system;
+ KernelCore& kernel;
+};
+
+inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
+ KThread* thread) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ tree->erase(tree->iterator_to(*thread));
+}
+
+inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
+ KThread* thread) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ tree->insert(*thread);
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp
new file mode 100644
index 000000000..bb2fa4ad5
--- /dev/null
+++ b/src/core/hle/kernel/k_event.cpp
@@ -0,0 +1,32 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
+
+namespace Kernel {
+
+KEvent::KEvent(KernelCore& kernel, std::string&& name) : Object{kernel, std::move(name)} {}
+
+KEvent::~KEvent() = default;
+
+std::shared_ptr<KEvent> KEvent::Create(KernelCore& kernel, std::string&& name) {
+ return std::make_shared<KEvent>(kernel, std::move(name));
+}
+
+void KEvent::Initialize() {
+ // Create our sub events.
+ readable_event = std::make_shared<KReadableEvent>(kernel, GetName() + ":Readable");
+ writable_event = std::make_shared<KWritableEvent>(kernel, GetName() + ":Writable");
+
+ // Initialize our sub sessions.
+ readable_event->Initialize(this);
+ writable_event->Initialize(this);
+
+ // Mark initialized.
+ initialized = true;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h
new file mode 100644
index 000000000..2fb887129
--- /dev/null
+++ b/src/core/hle/kernel/k_event.h
@@ -0,0 +1,57 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/object.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KReadableEvent;
+class KWritableEvent;
+
+class KEvent final : public Object {
+public:
+ explicit KEvent(KernelCore& kernel, std::string&& name);
+ ~KEvent() override;
+
+ static std::shared_ptr<KEvent> Create(KernelCore& kernel, std::string&& name);
+
+ void Initialize();
+
+ void Finalize() override {}
+
+ std::string GetTypeName() const override {
+ return "KEvent";
+ }
+
+ static constexpr HandleType HANDLE_TYPE = HandleType::Event;
+ HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
+ std::shared_ptr<KReadableEvent>& GetReadableEvent() {
+ return readable_event;
+ }
+
+ std::shared_ptr<KWritableEvent>& GetWritableEvent() {
+ return writable_event;
+ }
+
+ const std::shared_ptr<KReadableEvent>& GetReadableEvent() const {
+ return readable_event;
+ }
+
+ const std::shared_ptr<KWritableEvent>& GetWritableEvent() const {
+ return writable_event;
+ }
+
+private:
+ std::shared_ptr<KReadableEvent> readable_event;
+ std::shared_ptr<KWritableEvent> writable_event;
+ bool initialized{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h
new file mode 100644
index 000000000..362d0db28
--- /dev/null
+++ b/src/core/hle/kernel/k_light_condition_variable.h
@@ -0,0 +1,57 @@
+// 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"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_thread_queue.h"
+#include "core/hle/kernel/time_manager.h"
+
+namespace Kernel {
+class KernelCore;
+
+class KLightConditionVariable {
+public:
+ explicit KLightConditionVariable(KernelCore& kernel) : thread_queue(kernel), kernel(kernel) {}
+
+ void Wait(KLightLock* lock, s64 timeout = -1) {
+ WaitImpl(lock, timeout);
+ lock->Lock();
+ }
+
+ void Broadcast() {
+ KScopedSchedulerLock lk{kernel};
+ while (thread_queue.WakeupFrontThread() != nullptr) {
+ // We want to signal all threads, and so should continue waking up until there's nothing
+ // to wake.
+ }
+ }
+
+private:
+ void WaitImpl(KLightLock* lock, s64 timeout) {
+ KThread* owner = GetCurrentThreadPointer(kernel);
+
+ // Sleep the thread.
+ {
+ KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
+ lock->Unlock();
+
+ if (!thread_queue.SleepThread(owner)) {
+ lk.CancelSleep();
+ return;
+ }
+ }
+
+ // Cancel the task that the sleep setup.
+ kernel.TimeManager().UnscheduleTimeEvent(owner);
+ }
+ KThreadQueue thread_queue;
+ KernelCore& kernel;
+};
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
new file mode 100644
index 000000000..f974022e8
--- /dev/null
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -0,0 +1,130 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+void KLightLock::Lock() {
+ const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
+ const uintptr_t cur_thread_tag = (cur_thread | 1);
+
+ while (true) {
+ uintptr_t old_tag = tag.load(std::memory_order_relaxed);
+
+ while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
+ std::memory_order_acquire)) {
+ if ((old_tag | 1) == cur_thread_tag) {
+ return;
+ }
+ }
+
+ if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
+ break;
+ }
+
+ LockSlowPath(old_tag | 1, cur_thread);
+ }
+}
+
+void KLightLock::Unlock() {
+ const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
+ uintptr_t expected = cur_thread;
+ do {
+ if (expected != cur_thread) {
+ return UnlockSlowPath(cur_thread);
+ }
+ } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
+}
+
+void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
+ KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
+
+ // Pend the current thread waiting on the owner thread.
+ {
+ KScopedSchedulerLock sl{kernel};
+
+ // Ensure we actually have locking to do.
+ if (tag.load(std::memory_order_relaxed) != _owner) {
+ return;
+ }
+
+ // Add the current thread as a waiter on the owner.
+ KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
+ cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
+ owner_thread->AddWaiter(cur_thread);
+
+ // Set thread states.
+ if (cur_thread->GetState() == ThreadState::Runnable) {
+ cur_thread->SetState(ThreadState::Waiting);
+ } else {
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
+ }
+
+ if (owner_thread->IsSuspended()) {
+ owner_thread->ContinueIfHasKernelWaiters();
+ }
+ }
+
+ // We're no longer waiting on the lock owner.
+ {
+ KScopedSchedulerLock sl{kernel};
+ KThread* owner_thread = cur_thread->GetLockOwner();
+ if (owner_thread) {
+ owner_thread->RemoveWaiter(cur_thread);
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
+ }
+ }
+}
+
+void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
+ KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
+
+ // Unlock.
+ {
+ KScopedSchedulerLock sl{kernel};
+
+ // Get the next owner.
+ s32 num_waiters = 0;
+ KThread* next_owner = owner_thread->RemoveWaiterByKey(
+ std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
+
+ // Pass the lock to the next owner.
+ uintptr_t next_tag = 0;
+ if (next_owner) {
+ next_tag = reinterpret_cast<uintptr_t>(next_owner);
+ if (num_waiters > 1) {
+ next_tag |= 0x1;
+ }
+
+ if (next_owner->GetState() == ThreadState::Waiting) {
+ next_owner->SetState(ThreadState::Runnable);
+ } else {
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
+ }
+
+ if (next_owner->IsSuspended()) {
+ next_owner->ContinueIfHasKernelWaiters();
+ }
+ }
+
+ // We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
+ // so.
+ if (owner_thread->IsSuspended()) {
+ owner_thread->TrySuspend();
+ }
+
+ // Write the new tag value.
+ tag.store(next_tag);
+ }
+}
+
+bool KLightLock::IsLockedByCurrentThread() const {
+ return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL);
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h
new file mode 100644
index 000000000..f4c45f76a
--- /dev/null
+++ b/src/core/hle/kernel/k_light_lock.h
@@ -0,0 +1,41 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_scoped_lock.h"
+
+namespace Kernel {
+
+class KernelCore;
+
+class KLightLock {
+public:
+ explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {}
+
+ void Lock();
+
+ void Unlock();
+
+ void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
+
+ void UnlockSlowPath(uintptr_t cur_thread);
+
+ bool IsLocked() const {
+ return tag != 0;
+ }
+
+ bool IsLockedByCurrentThread() const;
+
+private:
+ std::atomic<uintptr_t> tag{};
+ KernelCore& kernel;
+};
+
+using KScopedLightLock = KScopedLock<KLightLock>;
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
index 99fb8fe93..4aa669d95 100644
--- a/src/core/hle/kernel/k_priority_queue.h
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -8,27 +8,27 @@
#pragma once
#include <array>
+#include <bit>
#include <concepts>
#include "common/assert.h"
#include "common/bit_set.h"
-#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/concepts.h"
namespace Kernel {
-class Thread;
+class KThread;
template <typename T>
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
{ t.GetAffinityMask() }
->Common::ConvertibleTo<u64>;
- {t.SetAffinityMask(std::declval<u64>())};
+ {t.SetAffinityMask(0)};
- { t.GetAffinity(std::declval<int32_t>()) }
+ { t.GetAffinity(0) }
->std::same_as<bool>;
- {t.SetAffinity(std::declval<int32_t>(), std::declval<bool>())};
+ {t.SetAffinity(0, false)};
{t.SetAll()};
};
@@ -42,11 +42,11 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
->std::same_as<T*>;
{ (typename T::QueueEntry()).GetPrev() }
->std::same_as<T*>;
- { t.GetPriorityQueueEntry(std::declval<s32>()) }
+ { t.GetPriorityQueueEntry(0) }
->std::same_as<typename T::QueueEntry&>;
{t.GetAffinityMask()};
- { typename std::remove_cvref<decltype(t.GetAffinityMask())>::type() }
+ { std::remove_cvref_t<decltype(t.GetAffinityMask())>() }
->KPriorityQueueAffinityMask;
{ t.GetActiveCore() }
@@ -55,17 +55,17 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
->Common::ConvertibleTo<s32>;
};
-template <typename Member, size_t _NumCores, int LowestPriority, int HighestPriority>
+template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
requires KPriorityQueueMember<Member> class KPriorityQueue {
public:
- using AffinityMaskType = typename std::remove_cv_t<
- typename std::remove_reference<decltype(std::declval<Member>().GetAffinityMask())>::type>;
+ using AffinityMaskType = std::remove_cv_t<
+ std::remove_reference_t<decltype(std::declval<Member>().GetAffinityMask())>>;
static_assert(LowestPriority >= 0);
static_assert(HighestPriority >= 0);
static_assert(LowestPriority >= HighestPriority);
static constexpr size_t NumPriority = LowestPriority - HighestPriority + 1;
- static constexpr size_t NumCores = _NumCores;
+ static constexpr size_t NumCores = NumCores_;
static constexpr bool IsValidCore(s32 core) {
return 0 <= core && core < static_cast<s32>(NumCores);
@@ -268,7 +268,7 @@ private:
}
constexpr s32 GetNextCore(u64& affinity) {
- const s32 core = Common::CountTrailingZeroes64(affinity);
+ const s32 core = std::countr_zero(affinity);
ClearAffinityBit(affinity, core);
return core;
}
@@ -367,7 +367,7 @@ public:
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
}
- constexpr Thread* MoveToScheduledBack(Member* member) {
+ constexpr KThread* MoveToScheduledBack(Member* member) {
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
member);
}
diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp
new file mode 100644
index 000000000..4b4d34857
--- /dev/null
+++ b/src/core/hle/kernel/k_readable_event.cpp
@@ -0,0 +1,56 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/logging/log.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+KReadableEvent::KReadableEvent(KernelCore& kernel, std::string&& name)
+ : KSynchronizationObject{kernel, std::move(name)} {}
+KReadableEvent::~KReadableEvent() = default;
+
+bool KReadableEvent::IsSignaled() const {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ return is_signaled;
+}
+
+ResultCode KReadableEvent::Signal() {
+ KScopedSchedulerLock lk{kernel};
+
+ if (!is_signaled) {
+ is_signaled = true;
+ NotifyAvailable();
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode KReadableEvent::Clear() {
+ Reset();
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode KReadableEvent::Reset() {
+ KScopedSchedulerLock lk{kernel};
+
+ if (!is_signaled) {
+ return ResultInvalidState;
+ }
+
+ is_signaled = false;
+ return RESULT_SUCCESS;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h
new file mode 100644
index 000000000..e6f0fd900
--- /dev/null
+++ b/src/core/hle/kernel/k_readable_event.h
@@ -0,0 +1,51 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KEvent;
+
+class KReadableEvent final : public KSynchronizationObject {
+public:
+ explicit KReadableEvent(KernelCore& kernel, std::string&& name);
+ ~KReadableEvent() override;
+
+ std::string GetTypeName() const override {
+ return "KReadableEvent";
+ }
+
+ static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
+ HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
+ KEvent* GetParent() const {
+ return parent;
+ }
+
+ void Initialize(KEvent* parent_) {
+ is_signaled = false;
+ parent = parent_;
+ }
+
+ bool IsSignaled() const override;
+ void Finalize() override {}
+
+ ResultCode Signal();
+ ResultCode Clear();
+ ResultCode Reset();
+
+private:
+ bool is_signaled{};
+ KEvent* parent{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
new file mode 100644
index 000000000..d7a4a38e6
--- /dev/null
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -0,0 +1,152 @@
+// 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 "common/assert.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
+
+KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system)
+ : Object{kernel}, lock{kernel}, cond_var{kernel}, kernel{kernel}, system(system) {}
+KResourceLimit::~KResourceLimit() = default;
+
+s64 KResourceLimit::GetLimitValue(LimitableResource which) const {
+ const auto index = static_cast<std::size_t>(which);
+ s64 value{};
+ {
+ KScopedLightLock lk{lock};
+ value = limit_values[index];
+ ASSERT(value >= 0);
+ ASSERT(current_values[index] <= limit_values[index]);
+ ASSERT(current_hints[index] <= current_values[index]);
+ }
+ return value;
+}
+
+s64 KResourceLimit::GetCurrentValue(LimitableResource which) const {
+ const auto index = static_cast<std::size_t>(which);
+ s64 value{};
+ {
+ KScopedLightLock lk{lock};
+ value = current_values[index];
+ ASSERT(value >= 0);
+ ASSERT(current_values[index] <= limit_values[index]);
+ ASSERT(current_hints[index] <= current_values[index]);
+ }
+ return value;
+}
+
+s64 KResourceLimit::GetPeakValue(LimitableResource which) const {
+ const auto index = static_cast<std::size_t>(which);
+ s64 value{};
+ {
+ KScopedLightLock lk{lock};
+ value = peak_values[index];
+ ASSERT(value >= 0);
+ ASSERT(current_values[index] <= limit_values[index]);
+ ASSERT(current_hints[index] <= current_values[index]);
+ }
+ return value;
+}
+
+s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
+ const auto index = static_cast<std::size_t>(which);
+ s64 value{};
+ {
+ KScopedLightLock lk(lock);
+ ASSERT(current_values[index] >= 0);
+ ASSERT(current_values[index] <= limit_values[index]);
+ ASSERT(current_hints[index] <= current_values[index]);
+ value = limit_values[index] - current_values[index];
+ }
+
+ return value;
+}
+
+ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
+ const auto index = static_cast<std::size_t>(which);
+ KScopedLightLock lk(lock);
+ R_UNLESS(current_values[index] <= value, ResultInvalidState);
+
+ limit_values[index] = value;
+
+ return RESULT_SUCCESS;
+}
+
+bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
+ return Reserve(which, value, system.CoreTiming().GetGlobalTimeNs().count() + DefaultTimeout);
+}
+
+bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
+ ASSERT(value >= 0);
+ const auto index = static_cast<std::size_t>(which);
+ KScopedLightLock lk(lock);
+
+ ASSERT(current_hints[index] <= current_values[index]);
+ if (current_hints[index] >= limit_values[index]) {
+ return false;
+ }
+
+ // Loop until we reserve or run out of time.
+ while (true) {
+ ASSERT(current_values[index] <= limit_values[index]);
+ ASSERT(current_hints[index] <= current_values[index]);
+
+ // If we would overflow, don't allow to succeed.
+ if (current_values[index] + value <= current_values[index]) {
+ break;
+ }
+
+ if (current_values[index] + value <= limit_values[index]) {
+ current_values[index] += value;
+ current_hints[index] += value;
+ peak_values[index] = std::max(peak_values[index], current_values[index]);
+ return true;
+ }
+
+ if (current_hints[index] + value <= limit_values[index] &&
+ (timeout < 0 || system.CoreTiming().GetGlobalTimeNs().count() < timeout)) {
+ waiter_count++;
+ cond_var.Wait(&lock, timeout);
+ waiter_count--;
+ } else {
+ break;
+ }
+ }
+
+ return false;
+}
+
+void KResourceLimit::Release(LimitableResource which, s64 value) {
+ Release(which, value, value);
+}
+
+void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
+ ASSERT(value >= 0);
+ ASSERT(hint >= 0);
+
+ const auto index = static_cast<std::size_t>(which);
+ KScopedLightLock lk(lock);
+ ASSERT(current_values[index] <= limit_values[index]);
+ ASSERT(current_hints[index] <= current_values[index]);
+ ASSERT(value <= current_values[index]);
+ ASSERT(hint <= current_hints[index]);
+
+ current_values[index] -= value;
+ current_hints[index] -= hint;
+
+ if (waiter_count != 0) {
+ cond_var.Broadcast();
+ }
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
new file mode 100644
index 000000000..58ae456f1
--- /dev/null
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -0,0 +1,81 @@
+// 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 "common/common_types.h"
+#include "core/hle/kernel/k_light_condition_variable.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/object.h"
+
+union ResultCode;
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KernelCore;
+enum class LimitableResource : u32 {
+ PhysicalMemory = 0,
+ Threads = 1,
+ Events = 2,
+ TransferMemory = 3,
+ Sessions = 4,
+
+ Count,
+};
+
+constexpr bool IsValidResourceType(LimitableResource type) {
+ return type < LimitableResource::Count;
+}
+
+class KResourceLimit final : public Object {
+public:
+ explicit KResourceLimit(KernelCore& kernel, Core::System& system);
+ ~KResourceLimit();
+
+ s64 GetLimitValue(LimitableResource which) const;
+ s64 GetCurrentValue(LimitableResource which) const;
+ s64 GetPeakValue(LimitableResource which) const;
+ s64 GetFreeValue(LimitableResource which) const;
+
+ ResultCode SetLimitValue(LimitableResource which, s64 value);
+
+ bool Reserve(LimitableResource which, s64 value);
+ bool Reserve(LimitableResource which, s64 value, s64 timeout);
+ void Release(LimitableResource which, s64 value);
+ void Release(LimitableResource which, s64 value, s64 hint);
+
+ std::string GetTypeName() const override {
+ return "KResourceLimit";
+ }
+ std::string GetName() const override {
+ return GetTypeName();
+ }
+
+ static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
+ HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
+ virtual void Finalize() override {}
+
+private:
+ using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>;
+ ResourceArray limit_values{};
+ ResourceArray current_values{};
+ ResourceArray current_hints{};
+ ResourceArray peak_values{};
+ mutable KLightLock lock;
+ s32 waiter_count{};
+ KLightConditionVariable cond_var;
+ KernelCore& kernel;
+ Core::System& system;
+};
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index c5fd82a6b..bb5f43b53 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -5,6 +5,8 @@
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+#include <bit>
+
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/fiber.h"
@@ -15,28 +17,33 @@
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
-static void IncrementScheduledCount(Kernel::Thread* thread) {
+static void IncrementScheduledCount(Kernel::KThread* thread) {
if (auto process = thread->GetOwnerProcess(); process) {
process->IncrementScheduledCount();
}
}
-void KScheduler::RescheduleCores(KernelCore& kernel, u64 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);
+void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
+ auto scheduler = kernel.CurrentScheduler();
+
+ u32 current_core{0xF};
+ bool must_context_switch{};
+ if (scheduler) {
+ current_core = scheduler->core_id;
+ // TODO(bunnei): Should be set to true when we deprecate single core
+ must_context_switch = !kernel.IsPhantomModeForSingleCore();
+ }
while (cores_pending_reschedule != 0) {
- u32 core = Common::CountTrailingZeroes64(cores_pending_reschedule);
+ const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
if (!must_context_switch || core != current_core) {
auto& phys_core = kernel.PhysicalCore(core);
@@ -54,28 +61,27 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
}
}
-u64 KScheduler::UpdateHighestPriorityThread(Thread* highest_thread) {
+u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
std::scoped_lock lock{guard};
- if (Thread* prev_highest_thread = this->state.highest_priority_thread;
+ if (KThread* prev_highest_thread = state.highest_priority_thread;
prev_highest_thread != highest_thread) {
if (prev_highest_thread != nullptr) {
IncrementScheduledCount(prev_highest_thread);
prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
}
- if (this->state.should_count_idle) {
+ if (state.should_count_idle) {
if (highest_thread != nullptr) {
- // if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
- // process->SetRunningThread(this->core_id, highest_thread,
- // this->state.idle_count);
- //}
+ if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
+ process->SetRunningThread(core_id, highest_thread, state.idle_count);
+ }
} else {
- this->state.idle_count++;
+ state.idle_count++;
}
}
- this->state.highest_priority_thread = highest_thread;
- this->state.needs_scheduling = true;
- return (1ULL << this->core_id);
+ state.highest_priority_thread = highest_thread;
+ state.needs_scheduling.store(true);
+ return (1ULL << core_id);
} else {
return 0;
}
@@ -88,16 +94,29 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
ClearSchedulerUpdateNeeded(kernel);
u64 cores_needing_scheduling = 0, idle_cores = 0;
- Thread* top_threads[Core::Hardware::NUM_CPU_CORES];
+ KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
/// We want to go over all cores, finding the highest priority thread and determining if
/// scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- Thread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
+ KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
// If the thread has no waiters, we need to check if the process has a thread pinned.
- // TODO(bunnei): Implement thread pinning
+ if (top_thread->GetNumKernelWaiters() == 0) {
+ if (Process* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
+ if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
+ pinned != nullptr && pinned != top_thread) {
+ // We prefer our parent's pinned thread if possible. However, we also don't
+ // want to schedule un-runnable threads.
+ if (pinned->GetRawState() == ThreadState::Runnable) {
+ top_thread = pinned;
+ } else {
+ top_thread = nullptr;
+ }
+ }
+ }
+ }
} else {
idle_cores |= (1ULL << core_id);
}
@@ -109,8 +128,8 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
- u32 core_id = Common::CountTrailingZeroes64(idle_cores);
- if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
+ const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
+ if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -118,7 +137,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
- if (Thread* top_thread =
+ if (KThread* top_thread =
(suggested_core >= 0) ? top_threads[suggested_core] : nullptr;
top_thread != suggested) {
// Make sure we're not dealing with threads too high priority for migration.
@@ -150,7 +169,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Check if there's some other thread that can run on the candidate core.
const s32 candidate_core = migration_candidates[i];
suggested = top_threads[candidate_core];
- if (Thread* next_on_candidate_core =
+ if (KThread* next_on_candidate_core =
priority_queue.GetScheduledNext(candidate_core, suggested);
next_on_candidate_core != nullptr) {
// The candidate core can run some other thread! We'll migrate its current
@@ -180,22 +199,35 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
-void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state) {
+void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
+ // Get an atomic reference to the core scheduler's previous thread.
+ std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
+ static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
+
+ // Atomically clear the previous thread if it's our target.
+ KThread* compare = thread;
+ prev_thread.compare_exchange_strong(compare, nullptr);
+ }
+}
+
+void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Check if the state has changed, because if it hasn't there's nothing to do.
- const auto cur_state = thread->scheduling_state;
+ const auto cur_state = thread->GetRawState();
if (cur_state == old_state) {
return;
}
// Update the priority queues.
- if (old_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ if (old_state == ThreadState::Runnable) {
// If we were previously runnable, then we're not runnable now, and we should remove.
GetPriorityQueue(kernel).Remove(thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
- } else if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ } else if (cur_state == ThreadState::Runnable) {
// If we're now runnable, then we weren't previously, and we should add.
GetPriorityQueue(kernel).PushBack(thread);
IncrementScheduledCount(thread);
@@ -203,13 +235,11 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 ol
}
}
-void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread,
- u32 old_priority) {
-
+void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is runnable, we want to change its priority in the queue.
- if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangePriority(
old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread);
IncrementScheduledCount(thread);
@@ -217,12 +247,12 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thr
}
}
-void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
+void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is runnable, we want to change its affinity in the queue.
- if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
@@ -237,8 +267,8 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
- Thread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
- Thread* next_thread = nullptr;
+ KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
+ KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
if (next_thread != top_thread) {
@@ -249,11 +279,11 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// While we have a suggested thread, try to migrate it!
{
- Thread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
- if (Thread* top_on_suggested_core =
+ if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -285,15 +315,15 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// Now that we might have migrated a thread with the same priority, check if we can do better.
{
- Thread* best_thread = priority_queue.GetScheduledFront(core_id);
+ KThread* best_thread = priority_queue.GetScheduledFront(core_id);
if (best_thread == GetCurrentThread()) {
best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
}
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
- if (best_thread != nullptr && best_thread->GetPriority() >= static_cast<u32>(priority)) {
- Thread* suggested = priority_queue.GetSuggestedFront(core_id);
+ if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -302,7 +332,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
- if (Thread* top_on_suggested_core =
+ if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -352,12 +382,14 @@ void KScheduler::DisableScheduling(KernelCore& kernel) {
}
}
-void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
- Core::EmuThreadHandle global_thread) {
+void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
- scheduler->GetCurrentThread()->EnableDispatch();
+ ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
+ if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
+ scheduler->GetCurrentThread()->EnableDispatch();
+ }
}
- RescheduleCores(kernel, cores_needing_scheduling, global_thread);
+ RescheduleCores(kernel, cores_needing_scheduling);
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -372,15 +404,13 @@ KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue;
}
-void KScheduler::YieldWithoutCoreMigration() {
- auto& kernel = system.Kernel();
-
+void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- Thread& cur_thread = *GetCurrentThread();
+ KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -395,10 +425,10 @@ void KScheduler::YieldWithoutCoreMigration() {
{
KScopedSchedulerLock lock(kernel);
- const auto cur_state = cur_thread.scheduling_state;
- if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ const auto cur_state = cur_thread.GetRawState();
+ if (cur_state == ThreadState::Runnable) {
// Put the current thread at the back of the queue.
- Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
+ KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
IncrementScheduledCount(std::addressof(cur_thread));
// If the next thread is different, we have an update to perform.
@@ -413,15 +443,13 @@ void KScheduler::YieldWithoutCoreMigration() {
}
}
-void KScheduler::YieldWithCoreMigration() {
- auto& kernel = system.Kernel();
-
+void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- Thread& cur_thread = *GetCurrentThread();
+ KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -436,23 +464,23 @@ void KScheduler::YieldWithCoreMigration() {
{
KScopedSchedulerLock lock(kernel);
- const auto cur_state = cur_thread.scheduling_state;
- if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ const auto cur_state = cur_thread.GetRawState();
+ if (cur_state == ThreadState::Runnable) {
// Get the current active core.
const s32 core_id = cur_thread.GetActiveCore();
// Put the current thread at the back of the queue.
- Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
+ KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
IncrementScheduledCount(std::addressof(cur_thread));
// While we have a suggested thread, try to migrate it!
bool recheck = false;
- Thread* suggested = priority_queue.GetSuggestedFront(core_id);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// Check if the suggested thread is the thread running on its core.
const s32 suggested_core = suggested->GetActiveCore();
- if (Thread* running_on_suggested_core =
+ if (KThread* running_on_suggested_core =
(suggested_core >= 0)
? kernel.Scheduler(suggested_core).state.highest_priority_thread
: nullptr;
@@ -503,15 +531,13 @@ void KScheduler::YieldWithCoreMigration() {
}
}
-void KScheduler::YieldToAnyThread() {
- auto& kernel = system.Kernel();
-
+void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- Thread& cur_thread = *GetCurrentThread();
+ KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -526,8 +552,8 @@ void KScheduler::YieldToAnyThread() {
{
KScopedSchedulerLock lock(kernel);
- const auto cur_state = cur_thread.scheduling_state;
- if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
+ const auto cur_state = cur_thread.GetRawState();
+ if (cur_state == ThreadState::Runnable) {
// Get the current active core.
const s32 core_id = cur_thread.GetActiveCore();
@@ -539,11 +565,11 @@ void KScheduler::YieldToAnyThread() {
// If there's nothing scheduled, we can try to perform a migration.
if (priority_queue.GetScheduledFront(core_id) == nullptr) {
// While we have a suggested thread, try to migrate it!
- Thread* suggested = priority_queue.GetSuggestedFront(core_id);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
- if (Thread* top_on_suggested_core =
+ if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -581,22 +607,21 @@ void KScheduler::YieldToAnyThread() {
}
}
-KScheduler::KScheduler(Core::System& system, std::size_t core_id)
- : system(system), core_id(core_id) {
+KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) {
switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
- this->state.needs_scheduling = true;
- this->state.interrupt_task_thread_runnable = false;
- this->state.should_count_idle = false;
- this->state.idle_count = 0;
- this->state.idle_thread_stack = nullptr;
- this->state.highest_priority_thread = nullptr;
+ state.needs_scheduling.store(true);
+ state.interrupt_task_thread_runnable = false;
+ state.should_count_idle = false;
+ state.idle_count = 0;
+ state.idle_thread_stack = nullptr;
+ state.highest_priority_thread = nullptr;
}
KScheduler::~KScheduler() = default;
-Thread* KScheduler::GetCurrentThread() const {
- if (current_thread) {
- return current_thread;
+KThread* KScheduler::GetCurrentThread() const {
+ if (auto result = current_thread.load(); result) {
+ return result;
}
return idle_thread;
}
@@ -613,7 +638,7 @@ void KScheduler::RescheduleCurrentCore() {
phys_core.ClearInterrupt();
}
guard.lock();
- if (this->state.needs_scheduling) {
+ if (state.needs_scheduling.load()) {
Schedule();
} else {
guard.unlock();
@@ -624,67 +649,76 @@ void KScheduler::OnThreadStart() {
SwitchContextStep2();
}
-void KScheduler::Unload(Thread* thread) {
+void KScheduler::Unload(KThread* thread) {
+ LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
+
if (thread) {
- thread->SetIsRunning(false);
- if (thread->IsContinuousOnSVC() && !thread->IsHLEThread()) {
+ if (thread->IsCallingSvc()) {
system.ArmInterface(core_id).ExceptionalExit();
- thread->SetContinuousOnSVC(false);
+ thread->ClearIsCallingSvc();
}
- if (!thread->IsHLEThread() && !thread->HasExited()) {
+ if (!thread->IsTerminationRequested()) {
+ prev_thread = thread;
+
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
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();
+ } else {
+ prev_thread = nullptr;
}
thread->context_guard.unlock();
}
}
-void KScheduler::Reload(Thread* thread) {
- if (thread) {
- ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
- "Thread must be runnable.");
+void KScheduler::Reload(KThread* thread) {
+ LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr");
- // Cancel any outstanding wakeup events for this thread
- thread->SetIsRunning(true);
- thread->SetWasRunning(false);
+ if (thread) {
+ ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
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 = system.ArmInterface(core_id);
- cpu_core.LoadContext(thread->GetContext32());
- cpu_core.LoadContext(thread->GetContext64());
- cpu_core.SetTlsAddress(thread->GetTLSAddress());
- cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
- }
+
+ Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
+ cpu_core.LoadContext(thread->GetContext32());
+ cpu_core.LoadContext(thread->GetContext64());
+ cpu_core.SetTlsAddress(thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
}
}
void KScheduler::SwitchContextStep2() {
// Load context of new thread
- Reload(current_thread);
+ Reload(current_thread.load());
RescheduleCurrentCore();
}
void KScheduler::ScheduleImpl() {
- Thread* previous_thread = current_thread;
- current_thread = state.highest_priority_thread;
+ KThread* previous_thread = current_thread.load();
+ KThread* next_thread = state.highest_priority_thread;
- this->state.needs_scheduling = false;
+ state.needs_scheduling = false;
+
+ // We never want to schedule a null thread, so use the idle thread if we don't have a next.
+ if (next_thread == nullptr) {
+ next_thread = idle_thread;
+ }
- if (current_thread == previous_thread) {
+ // If we're not actually switching thread, there's nothing to do.
+ if (next_thread == current_thread.load()) {
guard.unlock();
return;
}
+ current_thread.store(next_thread);
+
Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -715,28 +749,29 @@ void KScheduler::SwitchToCurrent() {
while (true) {
{
std::scoped_lock lock{guard};
- current_thread = state.highest_priority_thread;
- this->state.needs_scheduling = false;
+ current_thread.store(state.highest_priority_thread);
+ state.needs_scheduling.store(false);
}
const auto is_switch_pending = [this] {
std::scoped_lock lock{guard};
- return state.needs_scheduling.load(std::memory_order_relaxed);
+ return state.needs_scheduling.load();
};
do {
- if (current_thread != nullptr && !current_thread->IsHLEThread()) {
- current_thread->context_guard.lock();
- if (!current_thread->IsRunnable()) {
- current_thread->context_guard.unlock();
+ auto next_thread = current_thread.load();
+ if (next_thread != nullptr) {
+ next_thread->context_guard.lock();
+ if (next_thread->GetRawState() != ThreadState::Runnable) {
+ next_thread->context_guard.unlock();
break;
}
- if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) {
- current_thread->context_guard.unlock();
+ if (next_thread->GetActiveCore() != core_id) {
+ next_thread->context_guard.unlock();
break;
}
}
std::shared_ptr<Common::Fiber>* next_context;
- if (current_thread != nullptr) {
- next_context = &current_thread->GetHostContext();
+ if (next_thread != nullptr) {
+ next_context = &next_thread->GetHostContext();
} else {
next_context = &idle_thread->GetHostContext();
}
@@ -745,13 +780,13 @@ void KScheduler::SwitchToCurrent() {
}
}
-void KScheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
+void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
- thread->UpdateCPUTimeTicks(update_ticks);
+ thread->AddCpuTime(core_id, update_ticks);
}
if (process != nullptr) {
@@ -765,15 +800,10 @@ void KScheduler::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);
+ auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
+ KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
+ nullptr, std::move(init_func), init_func_parameter);
idle_thread = thread_res.Unwrap().get();
-
- {
- KScopedSchedulerLock lock{system.Kernel()};
- idle_thread->SetStatus(ThreadStatus::Ready);
- }
}
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index e84abc84c..f595b9a5c 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -29,29 +29,33 @@ namespace Kernel {
class KernelCore;
class Process;
class SchedulerLock;
-class Thread;
+class KThread;
class KScheduler final {
public:
- explicit KScheduler(Core::System& system, std::size_t core_id);
+ explicit KScheduler(Core::System& system, s32 core_id);
~KScheduler();
/// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore();
/// Reschedules cores pending reschedule, to be called on EnableScheduling.
- static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
- Core::EmuThreadHandle global_thread);
+ static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
/// The next two are for SingleCore Only.
/// Unload current thread before preempting core.
- void Unload(Thread* thread);
+ void Unload(KThread* thread);
/// Reload current thread after core preemption.
- void Reload(Thread* thread);
+ void Reload(KThread* thread);
/// Gets the current running thread
- [[nodiscard]] Thread* GetCurrentThread() const;
+ [[nodiscard]] KThread* GetCurrentThread() const;
+
+ /// Returns true if the scheduler is idle
+ [[nodiscard]] bool IsIdle() const {
+ return GetCurrentThread() == idle_thread;
+ }
/// Gets the timestamp for the last context switch in ticks.
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
@@ -72,14 +76,14 @@ public:
return switch_fiber;
}
- [[nodiscard]] u64 UpdateHighestPriorityThread(Thread* highest_thread);
+ [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
/**
* Takes a thread and moves it to the back of the it's priority list.
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
- void YieldWithoutCoreMigration();
+ static void YieldWithoutCoreMigration(KernelCore& kernel);
/**
* Takes a thread and moves it to the back of the it's priority list.
@@ -88,7 +92,7 @@ public:
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
- void YieldWithCoreMigration();
+ static void YieldWithCoreMigration(KernelCore& kernel);
/**
* Takes a thread and moves it out of the scheduling queue.
@@ -97,17 +101,18 @@ public:
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
- void YieldToAnyThread();
+ static void YieldToAnyThread(KernelCore& kernel);
+
+ static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
/// Notify the scheduler a thread's status has changed.
- static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state);
+ static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
/// Notify the scheduler a thread's priority has changed.
- static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread,
- u32 old_priority);
+ static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
/// Notify the scheduler a thread's core and/or affinity mask has changed.
- static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
+ static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
static bool CanSchedule(KernelCore& kernel);
@@ -115,8 +120,7 @@ public:
static void SetSchedulerUpdateNeeded(KernelCore& kernel);
static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
static void DisableScheduling(KernelCore& kernel);
- static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
- Core::EmuThreadHandle global_thread);
+ static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
[[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
private:
@@ -164,13 +168,15 @@ private:
* most recent tick count retrieved. No special arithmetic is
* applied to it.
*/
- void UpdateLastContextSwitchTime(Thread* thread, Process* process);
+ void UpdateLastContextSwitchTime(KThread* thread, Process* process);
static void OnSwitch(void* this_scheduler);
void SwitchToCurrent();
- Thread* current_thread{};
- Thread* idle_thread{};
+ KThread* prev_thread{};
+ std::atomic<KThread*> current_thread{};
+
+ KThread* idle_thread;
std::shared_ptr<Common::Fiber> switch_fiber{};
@@ -179,7 +185,7 @@ private:
bool interrupt_task_thread_runnable{};
bool should_count_idle{};
u64 idle_count{};
- Thread* highest_priority_thread{};
+ KThread* highest_priority_thread{};
void* idle_thread_stack{};
};
@@ -187,7 +193,7 @@ private:
Core::System& system;
u64 last_context_switch_time{};
- const std::size_t core_id;
+ const s32 core_id;
Common::SpinLock guard{};
};
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 2f1c1f691..169455d18 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -10,6 +10,7 @@
#include "common/assert.h"
#include "common/spin_lock.h"
#include "core/hardware_properties.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
@@ -19,49 +20,48 @@ class KernelCore;
template <typename SchedulerType>
class KAbstractSchedulerLock {
public:
- explicit KAbstractSchedulerLock(KernelCore& kernel) : kernel{kernel} {}
+ explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
bool IsLockedByCurrentThread() const {
- return this->owner_thread == kernel.GetCurrentEmuThreadID();
+ return owner_thread == GetCurrentThreadPointer(kernel);
}
void Lock() {
- if (this->IsLockedByCurrentThread()) {
+ if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count.
- ASSERT(this->lock_count > 0);
- this->lock_count++;
+ ASSERT(lock_count > 0);
+ lock_count++;
} else {
// Otherwise, we want to disable scheduling and acquire the spinlock.
SchedulerType::DisableScheduling(kernel);
- this->spin_lock.lock();
+ spin_lock.lock();
// For debug, ensure that our state is valid.
- ASSERT(this->lock_count == 0);
- ASSERT(this->owner_thread == Core::EmuThreadHandle::InvalidHandle());
+ ASSERT(lock_count == 0);
+ ASSERT(owner_thread == nullptr);
// Increment count, take ownership.
- this->lock_count = 1;
- this->owner_thread = kernel.GetCurrentEmuThreadID();
+ lock_count = 1;
+ owner_thread = GetCurrentThreadPointer(kernel);
}
}
void Unlock() {
- ASSERT(this->IsLockedByCurrentThread());
- ASSERT(this->lock_count > 0);
+ ASSERT(IsLockedByCurrentThread());
+ ASSERT(lock_count > 0);
// Release an instance of the lock.
- if ((--this->lock_count) == 0) {
+ if ((--lock_count) == 0) {
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel);
- Core::EmuThreadHandle leaving_thread = owner_thread;
// Note that we no longer hold the lock, and unlock the spinlock.
- this->owner_thread = Core::EmuThreadHandle::InvalidHandle();
- this->spin_lock.unlock();
+ owner_thread = nullptr;
+ spin_lock.unlock();
// Enable scheduling, and perform a rescheduling operation.
- SchedulerType::EnableScheduling(kernel, cores_needing_scheduling, leaving_thread);
+ SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
}
}
@@ -69,7 +69,7 @@ private:
KernelCore& kernel;
Common::SpinLock spin_lock{};
s32 lock_count{};
- Core::EmuThreadHandle owner_thread{Core::EmuThreadHandle::InvalidHandle()};
+ KThread* owner_thread{};
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scoped_resource_reservation.h b/src/core/hle/kernel/k_scoped_resource_reservation.h
new file mode 100644
index 000000000..c5deca00b
--- /dev/null
+++ b/src/core/hle/kernel/k_scoped_resource_reservation.h
@@ -0,0 +1,67 @@
+// Copyright 2021 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"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/process.h"
+
+namespace Kernel {
+
+class KScopedResourceReservation {
+public:
+ explicit KScopedResourceReservation(std::shared_ptr<KResourceLimit> l, LimitableResource r,
+ s64 v, s64 timeout)
+ : resource_limit(std::move(l)), value(v), resource(r) {
+ if (resource_limit && value) {
+ success = resource_limit->Reserve(resource, value, timeout);
+ } else {
+ success = true;
+ }
+ }
+
+ explicit KScopedResourceReservation(std::shared_ptr<KResourceLimit> l, LimitableResource r,
+ s64 v = 1)
+ : resource_limit(std::move(l)), value(v), resource(r) {
+ if (resource_limit && value) {
+ success = resource_limit->Reserve(resource, value);
+ } else {
+ success = true;
+ }
+ }
+
+ explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v, s64 t)
+ : KScopedResourceReservation(p->GetResourceLimit(), r, v, t) {}
+
+ explicit KScopedResourceReservation(const Process* p, LimitableResource r, s64 v = 1)
+ : KScopedResourceReservation(p->GetResourceLimit(), r, v) {}
+
+ ~KScopedResourceReservation() noexcept {
+ if (resource_limit && value && success) {
+ // resource was not committed, release the reservation.
+ resource_limit->Release(resource, value);
+ }
+ }
+
+ /// Commit the resource reservation, destruction of this object does not release the resource
+ void Commit() {
+ resource_limit = nullptr;
+ }
+
+ [[nodiscard]] bool Succeeded() const {
+ return success;
+ }
+
+private:
+ std::shared_ptr<KResourceLimit> resource_limit;
+ s64 value;
+ LimitableResource resource;
+ bool success;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 2bb3817fa..f8189e107 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -9,27 +9,24 @@
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class KScopedSchedulerLockAndSleep {
public:
- explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* t,
- s64 timeout)
- : kernel(kernel), event_handle(event_handle), thread(t), timeout_tick(timeout) {
- event_handle = InvalidHandle;
-
+ explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout)
+ : kernel(kernel), thread(t), timeout_tick(timeout) {
// Lock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
}
~KScopedSchedulerLockAndSleep() {
// Register the sleep.
- if (this->timeout_tick > 0) {
- kernel.TimeManager().ScheduleTimeEvent(event_handle, this->thread, this->timeout_tick);
+ if (timeout_tick > 0) {
+ kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
}
// Unlock the scheduler.
@@ -37,13 +34,12 @@ public:
}
void CancelSleep() {
- this->timeout_tick = 0;
+ timeout_tick = 0;
}
private:
KernelCore& kernel;
- Handle& event_handle;
- Thread* thread{};
+ KThread* thread{};
s64 timeout_tick{};
};
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
new file mode 100644
index 000000000..82f72a0fe
--- /dev/null
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -0,0 +1,171 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
+ KSynchronizationObject** objects, const s32 num_objects,
+ s64 timeout) {
+ // Allocate space on stack for thread nodes.
+ std::vector<ThreadListNode> thread_nodes(num_objects);
+
+ // Prepare for wait.
+ KThread* thread = kernel.CurrentScheduler()->GetCurrentThread();
+
+ {
+ // Setup the scheduling lock and sleep.
+ KScopedSchedulerLockAndSleep slp{kernel, thread, timeout};
+
+ // Check if any of the objects are already signaled.
+ for (auto i = 0; i < num_objects; ++i) {
+ ASSERT(objects[i] != nullptr);
+
+ if (objects[i]->IsSignaled()) {
+ *out_index = i;
+ slp.CancelSleep();
+ return RESULT_SUCCESS;
+ }
+ }
+
+ // Check if the timeout is zero.
+ if (timeout == 0) {
+ slp.CancelSleep();
+ return ResultTimedOut;
+ }
+
+ // Check if the thread should terminate.
+ if (thread->IsTerminationRequested()) {
+ slp.CancelSleep();
+ return ResultTerminationRequested;
+ }
+
+ // Check if waiting was canceled.
+ if (thread->IsWaitCancelled()) {
+ slp.CancelSleep();
+ thread->ClearWaitCancelled();
+ return ResultCancelled;
+ }
+
+ // Add the waiters.
+ for (auto i = 0; i < num_objects; ++i) {
+ thread_nodes[i].thread = thread;
+ thread_nodes[i].next = nullptr;
+
+ if (objects[i]->thread_list_tail == nullptr) {
+ objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
+ } else {
+ objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
+ }
+
+ objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
+ }
+
+ // For debugging only
+ thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
+
+ // Mark the thread as waiting.
+ thread->SetCancellable();
+ thread->SetSyncedObject(nullptr, ResultTimedOut);
+ thread->SetState(ThreadState::Waiting);
+ thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
+ }
+
+ // The lock/sleep is done, so we should be able to get our result.
+
+ // Thread is no longer cancellable.
+ thread->ClearCancellable();
+
+ // For debugging only
+ thread->SetWaitObjectsForDebugging({});
+
+ // Cancel the timer as needed.
+ kernel.TimeManager().UnscheduleTimeEvent(thread);
+
+ // Get the wait result.
+ ResultCode wait_result{RESULT_SUCCESS};
+ s32 sync_index = -1;
+ {
+ KScopedSchedulerLock lock(kernel);
+ KSynchronizationObject* synced_obj;
+ wait_result = thread->GetWaitResult(std::addressof(synced_obj));
+
+ for (auto i = 0; i < num_objects; ++i) {
+ // Unlink the object from the list.
+ ThreadListNode* prev_ptr =
+ reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
+ ThreadListNode* prev_val = nullptr;
+ ThreadListNode *prev, *tail_prev;
+
+ do {
+ prev = prev_ptr;
+ prev_ptr = prev_ptr->next;
+ tail_prev = prev_val;
+ prev_val = prev_ptr;
+ } while (prev_ptr != std::addressof(thread_nodes[i]));
+
+ if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
+ objects[i]->thread_list_tail = tail_prev;
+ }
+
+ prev->next = thread_nodes[i].next;
+
+ if (objects[i] == synced_obj) {
+ sync_index = i;
+ }
+ }
+ }
+
+ // Set output.
+ *out_index = sync_index;
+ return wait_result;
+}
+
+KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {}
+
+KSynchronizationObject::KSynchronizationObject(KernelCore& kernel, std::string&& name)
+ : Object{kernel, std::move(name)} {}
+
+KSynchronizationObject::~KSynchronizationObject() = default;
+
+void KSynchronizationObject::NotifyAvailable(ResultCode result) {
+ KScopedSchedulerLock lock(kernel);
+
+ // If we're not signaled, we've nothing to notify.
+ if (!this->IsSignaled()) {
+ return;
+ }
+
+ // Iterate over each thread.
+ for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
+ KThread* thread = cur_node->thread;
+ if (thread->GetState() == ThreadState::Waiting) {
+ thread->SetSyncedObject(this, result);
+ thread->SetState(ThreadState::Runnable);
+ }
+ }
+}
+
+std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
+ std::vector<KThread*> threads;
+
+ // If debugging, dump the list of waiters.
+ {
+ KScopedSchedulerLock lock(kernel);
+ for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
+ threads.emplace_back(cur_node->thread);
+ }
+ }
+
+ return threads;
+}
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
new file mode 100644
index 000000000..5803718fd
--- /dev/null
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -0,0 +1,59 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "core/hle/kernel/object.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+class Synchronization;
+class KThread;
+
+/// Class that represents a Kernel object that a thread can be waiting on
+class KSynchronizationObject : public Object {
+public:
+ struct ThreadListNode {
+ ThreadListNode* next{};
+ KThread* thread{};
+ };
+
+ [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
+ KSynchronizationObject** objects, const s32 num_objects,
+ s64 timeout);
+
+ [[nodiscard]] virtual bool IsSignaled() const = 0;
+
+ [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
+
+protected:
+ explicit KSynchronizationObject(KernelCore& kernel);
+ explicit KSynchronizationObject(KernelCore& kernel, std::string&& name);
+ virtual ~KSynchronizationObject();
+
+ void NotifyAvailable(ResultCode result);
+ void NotifyAvailable() {
+ return this->NotifyAvailable(RESULT_SUCCESS);
+ }
+
+private:
+ ThreadListNode* thread_list_head{};
+ ThreadListNode* thread_list_tail{};
+};
+
+// Specialization of DynamicObjectCast for KSynchronizationObjects
+template <>
+inline std::shared_ptr<KSynchronizationObject> DynamicObjectCast<KSynchronizationObject>(
+ std::shared_ptr<Object> object) {
+ if (object != nullptr && object->IsWaitable()) {
+ return std::static_pointer_cast<KSynchronizationObject>(object);
+ }
+ return nullptr;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
new file mode 100644
index 000000000..e5620da5a
--- /dev/null
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -0,0 +1,1048 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cinttypes>
+#include <optional>
+#include <vector>
+
+#include "common/assert.h"
+#include "common/bit_util.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/fiber.h"
+#include "common/logging/log.h"
+#include "common/scope_exit.h"
+#include "common/thread_queue_list.h"
+#include "core/core.h"
+#include "core/cpu_manager.h"
+#include "core/hardware_properties.h"
+#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory/memory_layout.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/svc_results.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 {
+static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
+ u32 entry_point, u32 arg) {
+ context = {};
+ context.cpu_registers[0] = arg;
+ context.cpu_registers[15] = entry_point;
+ context.cpu_registers[13] = stack_top;
+}
+
+static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
+ VAddr entry_point, u64 arg) {
+ context = {};
+ context.cpu_registers[0] = arg;
+ context.pc = entry_point;
+ context.sp = stack_top;
+ // TODO(merry): Perform a hardware test to determine the below value.
+ context.fpcr = 0;
+}
+} // namespace
+
+namespace Kernel {
+
+KThread::KThread(KernelCore& kernel)
+ : KSynchronizationObject{kernel}, activity_pause_lock{kernel} {}
+KThread::~KThread() = default;
+
+ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
+ s32 virt_core, Process* owner, ThreadType type) {
+ // Assert parameters are valid.
+ ASSERT((type == ThreadType::Main) ||
+ (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority));
+ ASSERT((owner != nullptr) || (type != ThreadType::User));
+ ASSERT(0 <= virt_core && virt_core < static_cast<s32>(Common::BitSize<u64>()));
+
+ // Convert the virtual core to a physical core.
+ const s32 phys_core = Core::Hardware::VirtualToPhysicalCoreMap[virt_core];
+ ASSERT(0 <= phys_core && phys_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
+
+ // First, clear the TLS address.
+ tls_address = {};
+
+ // Next, assert things based on the type.
+ switch (type) {
+ case ThreadType::Main:
+ ASSERT(arg == 0);
+ [[fallthrough]];
+ case ThreadType::HighPriority:
+ [[fallthrough]];
+ case ThreadType::User:
+ ASSERT(((owner == nullptr) ||
+ (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
+ ASSERT(((owner == nullptr) ||
+ (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask()));
+ break;
+ case ThreadType::Kernel:
+ UNIMPLEMENTED();
+ break;
+ default:
+ UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
+ break;
+ }
+ thread_type_for_debugging = type;
+
+ // Set the ideal core ID and affinity mask.
+ virtual_ideal_core_id = virt_core;
+ physical_ideal_core_id = phys_core;
+ virtual_affinity_mask = 1ULL << virt_core;
+ physical_affinity_mask.SetAffinity(phys_core, true);
+
+ // Set the thread state.
+ thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized;
+
+ // Set TLS address.
+ tls_address = 0;
+
+ // Set parent and condvar tree.
+ parent = nullptr;
+ condvar_tree = nullptr;
+
+ // Set sync booleans.
+ signaled = false;
+ termination_requested = false;
+ wait_cancelled = false;
+ cancellable = false;
+
+ // Set core ID and wait result.
+ core_id = phys_core;
+ wait_result = ResultNoSynchronizationObject;
+
+ // Set priorities.
+ priority = prio;
+ base_priority = prio;
+
+ // Set sync object and waiting lock to null.
+ synced_object = nullptr;
+
+ // Initialize sleeping queue.
+ sleeping_queue = nullptr;
+
+ // Set suspend flags.
+ suspend_request_flags = 0;
+ suspend_allowed_flags = static_cast<u32>(ThreadState::SuspendFlagMask);
+
+ // We're neither debug attached, nor are we nesting our priority inheritance.
+ debug_attached = false;
+ priority_inheritance_count = 0;
+
+ // We haven't been scheduled, and we have done no light IPC.
+ schedule_count = -1;
+ last_scheduled_tick = 0;
+ light_ipc_data = nullptr;
+
+ // We're not waiting for a lock, and we haven't disabled migration.
+ lock_owner = nullptr;
+ num_core_migration_disables = 0;
+
+ // We have no waiters, but we do have an entrypoint.
+ num_kernel_waiters = 0;
+
+ // Set our current core id.
+ current_core_id = phys_core;
+
+ // We haven't released our resource limit hint, and we've spent no time on the cpu.
+ resource_limit_release_hint = false;
+ cpu_time = 0;
+
+ // Clear our stack parameters.
+ std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
+ sizeof(StackParameters));
+
+ // Setup the TLS, if needed.
+ if (type == ThreadType::User) {
+ tls_address = owner->CreateTLSRegion();
+ }
+
+ // Set parent, if relevant.
+ if (owner != nullptr) {
+ parent = owner;
+ parent->IncrementThreadCount();
+ }
+
+ // Initialize thread context.
+ ResetThreadContext64(thread_context_64, user_stack_top, func, arg);
+ ResetThreadContext32(thread_context_32, static_cast<u32>(user_stack_top),
+ static_cast<u32>(func), static_cast<u32>(arg));
+
+ // Setup the stack parameters.
+ StackParameters& sp = GetStackParameters();
+ sp.cur_thread = this;
+ sp.disable_count = 1;
+ SetInExceptionHandler();
+
+ // Set thread ID.
+ thread_id = kernel.CreateNewThreadID();
+
+ // We initialized!
+ initialized = true;
+
+ // Register ourselves with our parent process.
+ if (parent != nullptr) {
+ parent->RegisterThread(this);
+ if (parent->IsSuspended()) {
+ RequestSuspend(SuspendType::Process);
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
+ VAddr user_stack_top, s32 prio, s32 core, Process* owner,
+ ThreadType type) {
+ // Initialize the thread.
+ R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
+
+ return RESULT_SUCCESS;
+}
+
+void KThread::Finalize() {
+ // If the thread has an owner process, unregister it.
+ if (parent != nullptr) {
+ parent->UnregisterThread(this);
+ }
+
+ // If the thread has a local region, delete it.
+ if (tls_address != 0) {
+ parent->FreeTLSRegion(tls_address);
+ }
+
+ // Release any waiters.
+ {
+ ASSERT(lock_owner == nullptr);
+ KScopedSchedulerLock sl{kernel};
+
+ auto it = waiter_list.begin();
+ while (it != waiter_list.end()) {
+ // The thread shouldn't be a kernel waiter.
+ it->SetLockOwner(nullptr);
+ it->SetSyncedObject(nullptr, ResultInvalidState);
+ it->Wakeup();
+ it = waiter_list.erase(it);
+ }
+ }
+
+ // Decrement the parent process's thread count.
+ if (parent != nullptr) {
+ parent->DecrementThreadCount();
+ parent->GetResourceLimit()->Release(LimitableResource::Threads, 1);
+ }
+}
+
+bool KThread::IsSignaled() const {
+ return signaled;
+}
+
+void KThread::Wakeup() {
+ KScopedSchedulerLock sl{kernel};
+
+ if (GetState() == ThreadState::Waiting) {
+ if (sleeping_queue != nullptr) {
+ sleeping_queue->WakeupThread(this);
+ } else {
+ SetState(ThreadState::Runnable);
+ }
+ }
+}
+
+void KThread::StartTermination() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Release user exception and unpin, if relevant.
+ if (parent != nullptr) {
+ parent->ReleaseUserException(this);
+ if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) {
+ parent->UnpinCurrentThread();
+ }
+ }
+
+ // Set state to terminated.
+ SetState(ThreadState::Terminated);
+
+ // Clear the thread's status as running in parent.
+ if (parent != nullptr) {
+ parent->ClearRunningThread(this);
+ }
+
+ // Signal.
+ signaled = true;
+ NotifyAvailable();
+
+ // Clear previous thread in KScheduler.
+ KScheduler::ClearPreviousThread(kernel, this);
+
+ // Register terminated dpc flag.
+ RegisterDpc(DpcFlag::Terminated);
+}
+
+void KThread::Pin() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Set ourselves as pinned.
+ GetStackParameters().is_pinned = true;
+
+ // Disable core migration.
+ ASSERT(num_core_migration_disables == 0);
+ {
+ ++num_core_migration_disables;
+
+ // Save our ideal state to restore when we're unpinned.
+ original_physical_ideal_core_id = physical_ideal_core_id;
+ original_physical_affinity_mask = physical_affinity_mask;
+
+ // Bind ourselves to this core.
+ const s32 active_core = GetActiveCore();
+ const s32 current_core = GetCurrentCoreId(kernel);
+
+ SetActiveCore(current_core);
+ physical_ideal_core_id = current_core;
+ physical_affinity_mask.SetAffinityMask(1ULL << current_core);
+
+ if (active_core != current_core || physical_affinity_mask.GetAffinityMask() !=
+ original_physical_affinity_mask.GetAffinityMask()) {
+ KScheduler::OnThreadAffinityMaskChanged(kernel, this, original_physical_affinity_mask,
+ active_core);
+ }
+ }
+
+ // Disallow performing thread suspension.
+ {
+ // Update our allow flags.
+ suspend_allowed_flags &= ~(1 << (static_cast<u32>(SuspendType::Thread) +
+ static_cast<u32>(ThreadState::SuspendShift)));
+
+ // Update our state.
+ const ThreadState old_state = thread_state;
+ thread_state = static_cast<ThreadState>(GetSuspendFlags() |
+ static_cast<u32>(old_state & ThreadState::Mask));
+ if (thread_state != old_state) {
+ KScheduler::OnThreadStateChanged(kernel, this, old_state);
+ }
+ }
+
+ // TODO(bunnei): Update our SVC access permissions.
+ ASSERT(parent != nullptr);
+}
+
+void KThread::Unpin() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Set ourselves as unpinned.
+ GetStackParameters().is_pinned = false;
+
+ // Enable core migration.
+ ASSERT(num_core_migration_disables == 1);
+ {
+ num_core_migration_disables--;
+
+ // Restore our original state.
+ const KAffinityMask old_mask = physical_affinity_mask;
+
+ physical_ideal_core_id = original_physical_ideal_core_id;
+ physical_affinity_mask = original_physical_affinity_mask;
+
+ if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
+ const s32 active_core = GetActiveCore();
+
+ if (!physical_affinity_mask.GetAffinity(active_core)) {
+ if (physical_ideal_core_id >= 0) {
+ SetActiveCore(physical_ideal_core_id);
+ } else {
+ SetActiveCore(static_cast<s32>(
+ Common::BitSize<u64>() - 1 -
+ std::countl_zero(physical_affinity_mask.GetAffinityMask())));
+ }
+ }
+ KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core);
+ }
+ }
+
+ // Allow performing thread suspension (if termination hasn't been requested).
+ {
+ // Update our allow flags.
+ if (!IsTerminationRequested()) {
+ suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) +
+ static_cast<u32>(ThreadState::SuspendShift)));
+ }
+
+ // Update our state.
+ const ThreadState old_state = thread_state;
+ thread_state = static_cast<ThreadState>(GetSuspendFlags() |
+ static_cast<u32>(old_state & ThreadState::Mask));
+ if (thread_state != old_state) {
+ KScheduler::OnThreadStateChanged(kernel, this, old_state);
+ }
+ }
+
+ // TODO(bunnei): Update our SVC access permissions.
+ ASSERT(parent != nullptr);
+
+ // Resume any threads that began waiting on us while we were pinned.
+ for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) {
+ if (it->GetState() == ThreadState::Waiting) {
+ it->SetState(ThreadState::Runnable);
+ }
+ }
+}
+
+ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
+ KScopedSchedulerLock sl{kernel};
+
+ // Get the virtual mask.
+ *out_ideal_core = virtual_ideal_core_id;
+ *out_affinity_mask = virtual_affinity_mask;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
+ KScopedSchedulerLock sl{kernel};
+ ASSERT(num_core_migration_disables >= 0);
+
+ // Select between core mask and original core mask.
+ if (num_core_migration_disables == 0) {
+ *out_ideal_core = physical_ideal_core_id;
+ *out_affinity_mask = physical_affinity_mask.GetAffinityMask();
+ } else {
+ *out_ideal_core = original_physical_ideal_core_id;
+ *out_affinity_mask = original_physical_affinity_mask.GetAffinityMask();
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) {
+ ASSERT(parent != nullptr);
+ ASSERT(v_affinity_mask != 0);
+ KScopedLightLock lk{activity_pause_lock};
+
+ // Set the core mask.
+ u64 p_affinity_mask = 0;
+ {
+ KScopedSchedulerLock sl{kernel};
+ ASSERT(num_core_migration_disables >= 0);
+
+ // If the core id is no-update magic, preserve the ideal core id.
+ if (core_id == Svc::IdealCoreNoUpdate) {
+ core_id = virtual_ideal_core_id;
+ R_UNLESS(((1ULL << core_id) & v_affinity_mask) != 0, ResultInvalidCombination);
+ }
+
+ // Set the virtual core/affinity mask.
+ virtual_ideal_core_id = core_id;
+ virtual_affinity_mask = v_affinity_mask;
+
+ // Translate the virtual core to a physical core.
+ if (core_id >= 0) {
+ core_id = Core::Hardware::VirtualToPhysicalCoreMap[core_id];
+ }
+
+ // Translate the virtual affinity mask to a physical one.
+ while (v_affinity_mask != 0) {
+ const u64 next = std::countr_zero(v_affinity_mask);
+ v_affinity_mask &= ~(1ULL << next);
+ p_affinity_mask |= (1ULL << Core::Hardware::VirtualToPhysicalCoreMap[next]);
+ }
+
+ // If we haven't disabled migration, perform an affinity change.
+ if (num_core_migration_disables == 0) {
+ const KAffinityMask old_mask = physical_affinity_mask;
+
+ // Set our new ideals.
+ physical_ideal_core_id = core_id;
+ physical_affinity_mask.SetAffinityMask(p_affinity_mask);
+
+ if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
+ const s32 active_core = GetActiveCore();
+
+ if (active_core >= 0 && !physical_affinity_mask.GetAffinity(active_core)) {
+ const s32 new_core = static_cast<s32>(
+ physical_ideal_core_id >= 0
+ ? physical_ideal_core_id
+ : Common::BitSize<u64>() - 1 -
+ std::countl_zero(physical_affinity_mask.GetAffinityMask()));
+ SetActiveCore(new_core);
+ }
+ KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core);
+ }
+ } else {
+ // Otherwise, we edit the original affinity for restoration later.
+ original_physical_ideal_core_id = core_id;
+ original_physical_affinity_mask.SetAffinityMask(p_affinity_mask);
+ }
+ }
+
+ // Update the pinned waiter list.
+ {
+ bool retry_update{};
+ bool thread_is_pinned{};
+ do {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl{kernel};
+
+ // Don't do any further management if our termination has been requested.
+ R_SUCCEED_IF(IsTerminationRequested());
+
+ // By default, we won't need to retry.
+ retry_update = false;
+
+ // Check if the thread is currently running.
+ bool thread_is_current{};
+ s32 thread_core;
+ for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES);
+ ++thread_core) {
+ if (kernel.Scheduler(thread_core).GetCurrentThread() == this) {
+ thread_is_current = true;
+ break;
+ }
+ }
+
+ // If the thread is currently running, check whether it's no longer allowed under the
+ // new mask.
+ if (thread_is_current && ((1ULL << thread_core) & p_affinity_mask) == 0) {
+ // If the thread is pinned, we want to wait until it's not pinned.
+ if (GetStackParameters().is_pinned) {
+ // Verify that the current thread isn't terminating.
+ R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
+ ResultTerminationRequested);
+
+ // Note that the thread was pinned.
+ thread_is_pinned = true;
+
+ // Wait until the thread isn't pinned any more.
+ pinned_waiter_list.push_back(GetCurrentThread(kernel));
+ GetCurrentThread(kernel).SetState(ThreadState::Waiting);
+ } else {
+ // If the thread isn't pinned, release the scheduler lock and retry until it's
+ // not current.
+ retry_update = true;
+ }
+ }
+ } while (retry_update);
+
+ // If the thread was pinned, it no longer is, and we should remove the current thread from
+ // our waiter list.
+ if (thread_is_pinned) {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl{kernel};
+
+ // Remove from the list.
+ pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+void KThread::SetBasePriority(s32 value) {
+ ASSERT(Svc::HighestThreadPriority <= value && value <= Svc::LowestThreadPriority);
+
+ KScopedSchedulerLock sl{kernel};
+
+ // Change our base priority.
+ base_priority = value;
+
+ // Perform a priority restoration.
+ RestorePriority(kernel, this);
+}
+
+void KThread::RequestSuspend(SuspendType type) {
+ KScopedSchedulerLock sl{kernel};
+
+ // Note the request in our flags.
+ suspend_request_flags |=
+ (1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)));
+
+ // Try to perform the suspend.
+ TrySuspend();
+}
+
+void KThread::Resume(SuspendType type) {
+ KScopedSchedulerLock sl{kernel};
+
+ // Clear the request in our flags.
+ suspend_request_flags &=
+ ~(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)));
+
+ // Update our state.
+ const ThreadState old_state = thread_state;
+ thread_state = static_cast<ThreadState>(GetSuspendFlags() |
+ static_cast<u32>(old_state & ThreadState::Mask));
+ if (thread_state != old_state) {
+ KScheduler::OnThreadStateChanged(kernel, this, old_state);
+ }
+}
+
+void KThread::WaitCancel() {
+ KScopedSchedulerLock sl{kernel};
+
+ // Check if we're waiting and cancellable.
+ if (GetState() == ThreadState::Waiting && cancellable) {
+ if (sleeping_queue != nullptr) {
+ sleeping_queue->WakeupThread(this);
+ wait_cancelled = true;
+ } else {
+ SetSyncedObject(nullptr, ResultCancelled);
+ SetState(ThreadState::Runnable);
+ wait_cancelled = false;
+ }
+ } else {
+ // Otherwise, note that we cancelled a wait.
+ wait_cancelled = true;
+ }
+}
+
+void KThread::TrySuspend() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSuspendRequested());
+
+ // Ensure that we have no waiters.
+ if (GetNumKernelWaiters() > 0) {
+ return;
+ }
+ ASSERT(GetNumKernelWaiters() == 0);
+
+ // Perform the suspend.
+ Suspend();
+}
+
+void KThread::Suspend() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSuspendRequested());
+
+ // Set our suspend flags in state.
+ const auto old_state = thread_state;
+ thread_state = static_cast<ThreadState>(GetSuspendFlags()) | (old_state & ThreadState::Mask);
+
+ // Note the state change in scheduler.
+ KScheduler::OnThreadStateChanged(kernel, this, old_state);
+}
+
+void KThread::Continue() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Clear our suspend flags in state.
+ const auto old_state = thread_state;
+ thread_state = old_state & ThreadState::Mask;
+
+ // Note the state change in scheduler.
+ KScheduler::OnThreadStateChanged(kernel, this, old_state);
+}
+
+ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
+ // Lock ourselves.
+ KScopedLightLock lk(activity_pause_lock);
+
+ // Set the activity.
+ {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl{kernel};
+
+ // Verify our state.
+ const auto cur_state = GetState();
+ R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
+ ResultInvalidState);
+
+ // Either pause or resume.
+ if (activity == Svc::ThreadActivity::Paused) {
+ // Verify that we're not suspended.
+ R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
+
+ // Suspend.
+ RequestSuspend(SuspendType::Thread);
+ } else {
+ ASSERT(activity == Svc::ThreadActivity::Runnable);
+
+ // Verify that we're suspended.
+ R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
+
+ // Resume.
+ Resume(SuspendType::Thread);
+ }
+ }
+
+ // If the thread is now paused, update the pinned waiter list.
+ if (activity == Svc::ThreadActivity::Paused) {
+ bool thread_is_pinned{};
+ bool thread_is_current{};
+ do {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl{kernel};
+
+ // Don't do any further management if our termination has been requested.
+ R_SUCCEED_IF(IsTerminationRequested());
+
+ // Check whether the thread is pinned.
+ if (GetStackParameters().is_pinned) {
+ // Verify that the current thread isn't terminating.
+ R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
+ ResultTerminationRequested);
+
+ // Note that the thread was pinned and not current.
+ thread_is_pinned = true;
+ thread_is_current = false;
+
+ // Wait until the thread isn't pinned any more.
+ pinned_waiter_list.push_back(GetCurrentThread(kernel));
+ GetCurrentThread(kernel).SetState(ThreadState::Waiting);
+ } else {
+ // Check if the thread is currently running.
+ // If it is, we'll need to retry.
+ thread_is_current = false;
+
+ for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
+ if (kernel.Scheduler(i).GetCurrentThread() == this) {
+ thread_is_current = true;
+ break;
+ }
+ }
+ }
+ } while (thread_is_current);
+
+ // If the thread was pinned, it no longer is, and we should remove the current thread from
+ // our waiter list.
+ if (thread_is_pinned) {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl{kernel};
+
+ // Remove from the list.
+ pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode KThread::GetThreadContext3(std::vector<u8>& out) {
+ // Lock ourselves.
+ KScopedLightLock lk{activity_pause_lock};
+
+ // Get the context.
+ {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl{kernel};
+
+ // Verify that we're suspended.
+ R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
+
+ // If we're not terminating, get the thread's user context.
+ if (!IsTerminationRequested()) {
+ if (parent->Is64BitProcess()) {
+ // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
+ auto context = GetContext64();
+ context.pstate &= 0xFF0FFE20;
+
+ out.resize(sizeof(context));
+ std::memcpy(out.data(), &context, sizeof(context));
+ } else {
+ // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
+ auto context = GetContext32();
+ context.cpsr &= 0xFF0FFE20;
+
+ out.resize(sizeof(context));
+ std::memcpy(out.data(), &context, sizeof(context));
+ }
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+void KThread::AddWaiterImpl(KThread* thread) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Find the right spot to insert the waiter.
+ auto it = waiter_list.begin();
+ while (it != waiter_list.end()) {
+ if (it->GetPriority() > thread->GetPriority()) {
+ break;
+ }
+ it++;
+ }
+
+ // Keep track of how many kernel waiters we have.
+ if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
+ ASSERT((num_kernel_waiters++) >= 0);
+ }
+
+ // Insert the waiter.
+ waiter_list.insert(it, *thread);
+ thread->SetLockOwner(this);
+}
+
+void KThread::RemoveWaiterImpl(KThread* thread) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Keep track of how many kernel waiters we have.
+ if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
+ ASSERT((num_kernel_waiters--) > 0);
+ }
+
+ // Remove the waiter.
+ waiter_list.erase(waiter_list.iterator_to(*thread));
+ thread->SetLockOwner(nullptr);
+}
+
+void KThread::RestorePriority(KernelCore& kernel, KThread* thread) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ while (true) {
+ // We want to inherit priority where possible.
+ s32 new_priority = thread->GetBasePriority();
+ if (thread->HasWaiters()) {
+ new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority());
+ }
+
+ // If the priority we would inherit is not different from ours, don't do anything.
+ if (new_priority == thread->GetPriority()) {
+ return;
+ }
+
+ // Ensure we don't violate condition variable red black tree invariants.
+ if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
+ BeforeUpdatePriority(kernel, cv_tree, thread);
+ }
+
+ // Change the priority.
+ const s32 old_priority = thread->GetPriority();
+ thread->SetPriority(new_priority);
+
+ // Restore the condition variable, if relevant.
+ if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
+ AfterUpdatePriority(kernel, cv_tree, thread);
+ }
+
+ // Update the scheduler.
+ KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority);
+
+ // Keep the lock owner up to date.
+ KThread* lock_owner = thread->GetLockOwner();
+ if (lock_owner == nullptr) {
+ return;
+ }
+
+ // Update the thread in the lock owner's sorted list, and continue inheriting.
+ lock_owner->RemoveWaiterImpl(thread);
+ lock_owner->AddWaiterImpl(thread);
+ thread = lock_owner;
+ }
+}
+
+void KThread::AddWaiter(KThread* thread) {
+ AddWaiterImpl(thread);
+ RestorePriority(kernel, this);
+}
+
+void KThread::RemoveWaiter(KThread* thread) {
+ RemoveWaiterImpl(thread);
+ RestorePriority(kernel, this);
+}
+
+KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ s32 num_waiters{};
+ KThread* next_lock_owner{};
+ auto it = waiter_list.begin();
+ while (it != waiter_list.end()) {
+ if (it->GetAddressKey() == key) {
+ KThread* thread = std::addressof(*it);
+
+ // Keep track of how many kernel waiters we have.
+ if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
+ ASSERT((num_kernel_waiters--) > 0);
+ }
+ it = waiter_list.erase(it);
+
+ // Update the next lock owner.
+ if (next_lock_owner == nullptr) {
+ next_lock_owner = thread;
+ next_lock_owner->SetLockOwner(nullptr);
+ } else {
+ next_lock_owner->AddWaiterImpl(thread);
+ }
+ num_waiters++;
+ } else {
+ it++;
+ }
+ }
+
+ // Do priority updates, if we have a next owner.
+ if (next_lock_owner) {
+ RestorePriority(kernel, this);
+ RestorePriority(kernel, next_lock_owner);
+ }
+
+ // Return output.
+ *out_num_waiters = num_waiters;
+ return next_lock_owner;
+}
+
+ResultCode KThread::Run() {
+ while (true) {
+ KScopedSchedulerLock lk{kernel};
+
+ // If either this thread or the current thread are requesting termination, note it.
+ R_UNLESS(!IsTerminationRequested(), ResultTerminationRequested);
+ R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
+
+ // Ensure our thread state is correct.
+ R_UNLESS(GetState() == ThreadState::Initialized, ResultInvalidState);
+
+ // If the current thread has been asked to suspend, suspend it and retry.
+ if (GetCurrentThread(kernel).IsSuspended()) {
+ GetCurrentThread(kernel).Suspend();
+ continue;
+ }
+
+ // If we're not a kernel thread and we've been asked to suspend, suspend ourselves.
+ if (IsUserThread() && IsSuspended()) {
+ Suspend();
+ }
+
+ // Set our state and finish.
+ SetState(ThreadState::Runnable);
+ return RESULT_SUCCESS;
+ }
+}
+
+void KThread::Exit() {
+ ASSERT(this == GetCurrentThreadPointer(kernel));
+
+ // Release the thread resource hint from parent.
+ if (parent != nullptr) {
+ // TODO(bunnei): Hint that the resource is about to be released.
+ resource_limit_release_hint = true;
+ }
+
+ // Perform termination.
+ {
+ KScopedSchedulerLock sl{kernel};
+
+ // Disallow all suspension.
+ suspend_allowed_flags = 0;
+
+ // Start termination.
+ StartTermination();
+ }
+}
+
+ResultCode KThread::Sleep(s64 timeout) {
+ ASSERT(!kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(this == GetCurrentThreadPointer(kernel));
+ ASSERT(timeout > 0);
+
+ {
+ // Setup the scheduling lock and sleep.
+ KScopedSchedulerLockAndSleep slp{kernel, this, timeout};
+
+ // Check if the thread should terminate.
+ if (IsTerminationRequested()) {
+ slp.CancelSleep();
+ return ResultTerminationRequested;
+ }
+
+ // Mark the thread as waiting.
+ SetState(ThreadState::Waiting);
+ SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
+ }
+
+ // The lock/sleep is done.
+
+ // Cancel the timer.
+ kernel.TimeManager().UnscheduleTimeEvent(this);
+
+ return RESULT_SUCCESS;
+}
+
+void KThread::SetState(ThreadState state) {
+ KScopedSchedulerLock sl{kernel};
+
+ // Clear debugging state
+ SetMutexWaitAddressForDebugging({});
+ SetWaitReasonForDebugging({});
+
+ const ThreadState old_state = thread_state;
+ thread_state =
+ static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
+ if (thread_state != old_state) {
+ KScheduler::OnThreadStateChanged(kernel, this, old_state);
+ }
+}
+
+std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
+ return host_context;
+}
+
+ResultVal<std::shared_ptr<KThread>> KThread::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<KThread>> KThread::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();
+
+ std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel);
+
+ if (const auto result =
+ thread->InitializeThread(thread.get(), entry_point, arg, stack_top, priority,
+ processor_id, owner_process, type_flags);
+ result.IsError()) {
+ return result;
+ }
+
+ thread->name = name;
+
+ auto& scheduler = kernel.GlobalSchedulerContext();
+ scheduler.AddThread(thread);
+
+ thread->host_context =
+ std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
+
+ return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
+}
+
+KThread* GetCurrentThreadPointer(KernelCore& kernel) {
+ return kernel.GetCurrentEmuThread();
+}
+
+KThread& GetCurrentThread(KernelCore& kernel) {
+ return *GetCurrentThreadPointer(kernel);
+}
+
+s32 GetCurrentCoreId(KernelCore& kernel) {
+ return GetCurrentThread(kernel).GetCurrentCore();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
new file mode 100644
index 000000000..c8ac656a4
--- /dev/null
+++ b/src/core/hle/kernel/k_thread.h
@@ -0,0 +1,768 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <span>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <boost/intrusive/list.hpp>
+
+#include "common/common_types.h"
+#include "common/intrusive_red_black_tree.h"
+#include "common/spin_lock.h"
+#include "core/arm/arm_interface.h"
+#include "core/hle/kernel/k_affinity_mask.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/svc_common.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Common {
+class Fiber;
+}
+
+namespace Core {
+class ARM_Interface;
+class System;
+} // namespace Core
+
+namespace Kernel {
+
+class GlobalSchedulerContext;
+class KernelCore;
+class Process;
+class KScheduler;
+class KThreadQueue;
+
+using KThreadFunction = VAddr;
+
+enum class ThreadType : u32 {
+ Main = 0,
+ Kernel = 1,
+ HighPriority = 2,
+ User = 3,
+};
+DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
+
+enum class SuspendType : u32 {
+ Process = 0,
+ Thread = 1,
+ Debug = 2,
+ Backtrace = 3,
+ Init = 4,
+
+ Count,
+};
+
+enum class ThreadState : u16 {
+ Initialized = 0,
+ Waiting = 1,
+ Runnable = 2,
+ Terminated = 3,
+
+ SuspendShift = 4,
+ Mask = (1 << SuspendShift) - 1,
+
+ ProcessSuspended = (1 << (0 + SuspendShift)),
+ ThreadSuspended = (1 << (1 + SuspendShift)),
+ DebugSuspended = (1 << (2 + SuspendShift)),
+ BacktraceSuspended = (1 << (3 + SuspendShift)),
+ InitSuspended = (1 << (4 + SuspendShift)),
+
+ SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
+};
+DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
+
+enum class DpcFlag : u32 {
+ Terminating = (1 << 0),
+ Terminated = (1 << 1),
+};
+
+enum class ThreadWaitReasonForDebugging : u32 {
+ None, ///< Thread is not waiting
+ Sleep, ///< Thread is waiting due to a SleepThread SVC
+ IPC, ///< Thread is waiting for the reply from an IPC request
+ Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC
+ ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC
+ Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC
+ Suspended, ///< Thread is waiting due to process suspension
+};
+
+[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
+[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
+[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
+
+class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
+ friend class KScheduler;
+ friend class Process;
+
+public:
+ static constexpr s32 DefaultThreadPriority = 44;
+ static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1;
+
+ explicit KThread(KernelCore& kernel);
+ ~KThread() override;
+
+public:
+ using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
+ using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
+ using WaiterList = boost::intrusive::list<KThread>;
+
+ /**
+ * 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
+ */
+ [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> 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 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
+ * @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
+ */
+ [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> 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);
+
+ [[nodiscard]] std::string GetName() const override {
+ return name;
+ }
+
+ void SetName(std::string new_name) {
+ name = std::move(new_name);
+ }
+
+ [[nodiscard]] std::string GetTypeName() const override {
+ return "Thread";
+ }
+
+ static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
+ [[nodiscard]] HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
+ /**
+ * Gets the thread's current priority
+ * @return The current thread's priority
+ */
+ [[nodiscard]] s32 GetPriority() const {
+ return priority;
+ }
+
+ /**
+ * Sets the thread's current priority.
+ * @param priority The new priority.
+ */
+ void SetPriority(s32 value) {
+ priority = value;
+ }
+
+ /**
+ * Gets the thread's nominal priority.
+ * @return The current thread's nominal priority.
+ */
+ [[nodiscard]] s32 GetBasePriority() const {
+ return base_priority;
+ }
+
+ /**
+ * Gets the thread's thread ID
+ * @return The thread's ID
+ */
+ [[nodiscard]] u64 GetThreadID() const {
+ return thread_id;
+ }
+
+ void ContinueIfHasKernelWaiters() {
+ if (GetNumKernelWaiters() > 0) {
+ Continue();
+ }
+ }
+
+ void Wakeup();
+
+ void SetBasePriority(s32 value);
+
+ [[nodiscard]] ResultCode Run();
+
+ void Exit();
+
+ [[nodiscard]] u32 GetSuspendFlags() const {
+ return suspend_allowed_flags & suspend_request_flags;
+ }
+
+ [[nodiscard]] bool IsSuspended() const {
+ return GetSuspendFlags() != 0;
+ }
+
+ [[nodiscard]] bool IsSuspendRequested(SuspendType type) const {
+ return (suspend_request_flags &
+ (1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)))) !=
+ 0;
+ }
+
+ [[nodiscard]] bool IsSuspendRequested() const {
+ return suspend_request_flags != 0;
+ }
+
+ void RequestSuspend(SuspendType type);
+
+ void Resume(SuspendType type);
+
+ void TrySuspend();
+
+ void Continue();
+
+ void Suspend();
+
+ void Finalize() override;
+
+ bool IsSignaled() const override;
+
+ void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
+ synced_object = obj;
+ wait_result = wait_res;
+ }
+
+ [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
+ *out = synced_object;
+ return wait_result;
+ }
+
+ /*
+ * Returns the Thread Local Storage address of the current thread
+ * @returns VAddr of the thread's TLS
+ */
+ [[nodiscard]] VAddr GetTLSAddress() const {
+ return tls_address;
+ }
+
+ /*
+ * Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
+ * @returns The value of the TPIDR_EL0 register.
+ */
+ [[nodiscard]] u64 GetTPIDR_EL0() const {
+ return thread_context_64.tpidr;
+ }
+
+ /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
+ void SetTPIDR_EL0(u64 value) {
+ thread_context_64.tpidr = value;
+ thread_context_32.tpidr = static_cast<u32>(value);
+ }
+
+ [[nodiscard]] ThreadContext32& GetContext32() {
+ return thread_context_32;
+ }
+
+ [[nodiscard]] const ThreadContext32& GetContext32() const {
+ return thread_context_32;
+ }
+
+ [[nodiscard]] ThreadContext64& GetContext64() {
+ return thread_context_64;
+ }
+
+ [[nodiscard]] const ThreadContext64& GetContext64() const {
+ return thread_context_64;
+ }
+
+ [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
+
+ [[nodiscard]] ThreadState GetState() const {
+ return thread_state & ThreadState::Mask;
+ }
+
+ [[nodiscard]] ThreadState GetRawState() const {
+ return thread_state;
+ }
+
+ void SetState(ThreadState state);
+
+ [[nodiscard]] s64 GetLastScheduledTick() const {
+ return last_scheduled_tick;
+ }
+
+ void SetLastScheduledTick(s64 tick) {
+ last_scheduled_tick = tick;
+ }
+
+ void AddCpuTime([[maybe_unused]] s32 core_id_, s64 amount) {
+ cpu_time += amount;
+ // TODO(bunnei): Debug kernels track per-core tick counts. Should we?
+ }
+
+ [[nodiscard]] s64 GetCpuTime() const {
+ return cpu_time;
+ }
+
+ [[nodiscard]] s32 GetActiveCore() const {
+ return core_id;
+ }
+
+ void SetActiveCore(s32 core) {
+ core_id = core;
+ }
+
+ [[nodiscard]] s32 GetCurrentCore() const {
+ return current_core_id;
+ }
+
+ void SetCurrentCore(s32 core) {
+ current_core_id = core;
+ }
+
+ [[nodiscard]] Process* GetOwnerProcess() {
+ return parent;
+ }
+
+ [[nodiscard]] const Process* GetOwnerProcess() const {
+ return parent;
+ }
+
+ [[nodiscard]] bool IsUserThread() const {
+ return parent != nullptr;
+ }
+
+ [[nodiscard]] KThread* GetLockOwner() const {
+ return lock_owner;
+ }
+
+ void SetLockOwner(KThread* owner) {
+ lock_owner = owner;
+ }
+
+ [[nodiscard]] const KAffinityMask& GetAffinityMask() const {
+ return physical_affinity_mask;
+ }
+
+ [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
+
+ [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
+
+ [[nodiscard]] ResultCode SetCoreMask(s32 core_id, u64 v_affinity_mask);
+
+ [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity);
+
+ [[nodiscard]] ResultCode Sleep(s64 timeout);
+
+ [[nodiscard]] s64 GetYieldScheduleCount() const {
+ return schedule_count;
+ }
+
+ void SetYieldScheduleCount(s64 count) {
+ schedule_count = count;
+ }
+
+ void WaitCancel();
+
+ [[nodiscard]] bool IsWaitCancelled() const {
+ return wait_cancelled;
+ }
+
+ [[nodiscard]] void ClearWaitCancelled() {
+ wait_cancelled = false;
+ }
+
+ [[nodiscard]] bool IsCancellable() const {
+ return cancellable;
+ }
+
+ void SetCancellable() {
+ cancellable = true;
+ }
+
+ void ClearCancellable() {
+ cancellable = false;
+ }
+
+ [[nodiscard]] bool IsTerminationRequested() const {
+ return termination_requested || GetRawState() == ThreadState::Terminated;
+ }
+
+ struct StackParameters {
+ u8 svc_permission[0x10];
+ std::atomic<u8> dpc_flags;
+ u8 current_svc_id;
+ bool is_calling_svc;
+ bool is_in_exception_handler;
+ bool is_pinned;
+ s32 disable_count;
+ KThread* cur_thread;
+ };
+
+ [[nodiscard]] StackParameters& GetStackParameters() {
+ return stack_parameters;
+ }
+
+ [[nodiscard]] const StackParameters& GetStackParameters() const {
+ return stack_parameters;
+ }
+
+ class QueueEntry {
+ public:
+ constexpr QueueEntry() = default;
+
+ constexpr void Initialize() {
+ prev = nullptr;
+ next = nullptr;
+ }
+
+ constexpr KThread* GetPrev() const {
+ return prev;
+ }
+ constexpr KThread* GetNext() const {
+ return next;
+ }
+ constexpr void SetPrev(KThread* thread) {
+ prev = thread;
+ }
+ constexpr void SetNext(KThread* thread) {
+ next = thread;
+ }
+
+ private:
+ KThread* prev{};
+ KThread* next{};
+ };
+
+ [[nodiscard]] QueueEntry& GetPriorityQueueEntry(s32 core) {
+ return per_core_priority_queue_entry[core];
+ }
+
+ [[nodiscard]] const QueueEntry& GetPriorityQueueEntry(s32 core) const {
+ return per_core_priority_queue_entry[core];
+ }
+
+ void SetSleepingQueue(KThreadQueue* q) {
+ sleeping_queue = q;
+ }
+
+ [[nodiscard]] s32 GetDisableDispatchCount() const {
+ return this->GetStackParameters().disable_count;
+ }
+
+ void DisableDispatch() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
+ this->GetStackParameters().disable_count++;
+ }
+
+ void EnableDispatch() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
+ this->GetStackParameters().disable_count--;
+ }
+
+ void Pin();
+
+ void Unpin();
+
+ void SetInExceptionHandler() {
+ this->GetStackParameters().is_in_exception_handler = true;
+ }
+
+ void ClearInExceptionHandler() {
+ this->GetStackParameters().is_in_exception_handler = false;
+ }
+
+ [[nodiscard]] bool IsInExceptionHandler() const {
+ return this->GetStackParameters().is_in_exception_handler;
+ }
+
+ void SetIsCallingSvc() {
+ this->GetStackParameters().is_calling_svc = true;
+ }
+
+ void ClearIsCallingSvc() {
+ this->GetStackParameters().is_calling_svc = false;
+ }
+
+ [[nodiscard]] bool IsCallingSvc() const {
+ return this->GetStackParameters().is_calling_svc;
+ }
+
+ [[nodiscard]] u8 GetSvcId() const {
+ return this->GetStackParameters().current_svc_id;
+ }
+
+ void RegisterDpc(DpcFlag flag) {
+ this->GetStackParameters().dpc_flags |= static_cast<u8>(flag);
+ }
+
+ void ClearDpc(DpcFlag flag) {
+ this->GetStackParameters().dpc_flags &= ~static_cast<u8>(flag);
+ }
+
+ [[nodiscard]] u8 GetDpc() const {
+ return this->GetStackParameters().dpc_flags;
+ }
+
+ [[nodiscard]] bool HasDpc() const {
+ return this->GetDpc() != 0;
+ }
+
+ void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
+ wait_reason_for_debugging = reason;
+ }
+
+ [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const {
+ return wait_reason_for_debugging;
+ }
+
+ [[nodiscard]] ThreadType GetThreadTypeForDebugging() const {
+ return thread_type_for_debugging;
+ }
+
+ void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
+ wait_objects_for_debugging.clear();
+ wait_objects_for_debugging.reserve(objects.size());
+ for (const auto& object : objects) {
+ wait_objects_for_debugging.emplace_back(object);
+ }
+ }
+
+ [[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
+ return wait_objects_for_debugging;
+ }
+
+ void SetMutexWaitAddressForDebugging(VAddr address) {
+ mutex_wait_address_for_debugging = address;
+ }
+
+ [[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
+ return mutex_wait_address_for_debugging;
+ }
+
+ [[nodiscard]] s32 GetIdealCoreForDebugging() const {
+ return virtual_ideal_core_id;
+ }
+
+ void AddWaiter(KThread* thread);
+
+ void RemoveWaiter(KThread* thread);
+
+ [[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out);
+
+ [[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
+
+ [[nodiscard]] VAddr GetAddressKey() const {
+ return address_key;
+ }
+
+ [[nodiscard]] u32 GetAddressKeyValue() const {
+ return address_key_value;
+ }
+
+ void SetAddressKey(VAddr key) {
+ address_key = key;
+ }
+
+ void SetAddressKey(VAddr key, u32 val) {
+ address_key = key;
+ address_key_value = val;
+ }
+
+ [[nodiscard]] bool HasWaiters() const {
+ return !waiter_list.empty();
+ }
+
+ [[nodiscard]] s32 GetNumKernelWaiters() const {
+ return num_kernel_waiters;
+ }
+
+ [[nodiscard]] u64 GetConditionVariableKey() const {
+ return condvar_key;
+ }
+
+ [[nodiscard]] u64 GetAddressArbiterKey() const {
+ return condvar_key;
+ }
+
+private:
+ static constexpr size_t PriorityInheritanceCountMax = 10;
+ union SyncObjectBuffer {
+ std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
+ std::array<Handle,
+ Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
+ handles;
+ constexpr SyncObjectBuffer() {}
+ };
+ static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
+
+ struct ConditionVariableComparator {
+ struct LightCompareType {
+ u64 cv_key{};
+ s32 priority{};
+
+ [[nodiscard]] constexpr u64 GetConditionVariableKey() const {
+ return cv_key;
+ }
+
+ [[nodiscard]] constexpr s32 GetPriority() const {
+ return priority;
+ }
+ };
+
+ template <typename T>
+ requires(
+ std::same_as<T, KThread> ||
+ std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
+ const KThread& rhs) {
+ const u64 l_key = lhs.GetConditionVariableKey();
+ const u64 r_key = rhs.GetConditionVariableKey();
+
+ if (l_key < r_key) {
+ // Sort first by key
+ return -1;
+ } else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
+ // And then by priority.
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ };
+
+ void AddWaiterImpl(KThread* thread);
+
+ void RemoveWaiterImpl(KThread* thread);
+
+ void StartTermination();
+
+ [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
+ s32 prio, s32 virt_core, Process* owner, ThreadType type);
+
+ [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func,
+ uintptr_t arg, VAddr user_stack_top, s32 prio,
+ s32 core, Process* owner, ThreadType type);
+
+ static void RestorePriority(KernelCore& kernel, KThread* thread);
+
+ // For core KThread implementation
+ ThreadContext32 thread_context_32{};
+ ThreadContext64 thread_context_64{};
+ Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
+ s32 priority{};
+ using ConditionVariableThreadTreeTraits =
+ Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<
+ &KThread::condvar_arbiter_tree_node>;
+ using ConditionVariableThreadTree =
+ ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
+ ConditionVariableThreadTree* condvar_tree{};
+ u64 condvar_key{};
+ u64 virtual_affinity_mask{};
+ KAffinityMask physical_affinity_mask{};
+ u64 thread_id{};
+ std::atomic<s64> cpu_time{};
+ KSynchronizationObject* synced_object{};
+ VAddr address_key{};
+ Process* parent{};
+ VAddr kernel_stack_top{};
+ u32* light_ipc_data{};
+ VAddr tls_address{};
+ KLightLock activity_pause_lock;
+ s64 schedule_count{};
+ s64 last_scheduled_tick{};
+ std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
+ KThreadQueue* sleeping_queue{};
+ WaiterList waiter_list{};
+ WaiterList pinned_waiter_list{};
+ KThread* lock_owner{};
+ u32 address_key_value{};
+ u32 suspend_request_flags{};
+ u32 suspend_allowed_flags{};
+ ResultCode wait_result{RESULT_SUCCESS};
+ s32 base_priority{};
+ s32 physical_ideal_core_id{};
+ s32 virtual_ideal_core_id{};
+ s32 num_kernel_waiters{};
+ s32 current_core_id{};
+ s32 core_id{};
+ KAffinityMask original_physical_affinity_mask{};
+ s32 original_physical_ideal_core_id{};
+ s32 num_core_migration_disables{};
+ ThreadState thread_state{};
+ std::atomic<bool> termination_requested{};
+ bool wait_cancelled{};
+ bool cancellable{};
+ bool signaled{};
+ bool initialized{};
+ bool debug_attached{};
+ s8 priority_inheritance_count{};
+ bool resource_limit_release_hint{};
+ StackParameters stack_parameters{};
+ Common::SpinLock context_guard{};
+
+ // For emulation
+ std::shared_ptr<Common::Fiber> host_context{};
+
+ // For debugging
+ std::vector<KSynchronizationObject*> wait_objects_for_debugging;
+ VAddr mutex_wait_address_for_debugging{};
+ ThreadWaitReasonForDebugging wait_reason_for_debugging{};
+ ThreadType thread_type_for_debugging{};
+ std::string name;
+
+public:
+ using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
+
+ void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, u64 cv_key,
+ u32 value) {
+ condvar_tree = tree;
+ condvar_key = cv_key;
+ address_key = address;
+ address_key_value = value;
+ }
+
+ void ClearConditionVariable() {
+ condvar_tree = nullptr;
+ }
+
+ [[nodiscard]] bool IsWaitingForConditionVariable() const {
+ return condvar_tree != nullptr;
+ }
+
+ void SetAddressArbiter(ConditionVariableThreadTree* tree, u64 address) {
+ condvar_tree = tree;
+ condvar_key = address;
+ }
+
+ void ClearAddressArbiter() {
+ condvar_tree = nullptr;
+ }
+
+ [[nodiscard]] bool IsWaitingForAddressArbiter() const {
+ return condvar_tree != nullptr;
+ }
+
+ [[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
+ return condvar_tree;
+ }
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
new file mode 100644
index 000000000..c52eba249
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -0,0 +1,81 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/k_thread.h"
+
+namespace Kernel {
+
+class KThreadQueue {
+public:
+ explicit KThreadQueue(KernelCore& kernel) : kernel{kernel} {}
+
+ bool IsEmpty() const {
+ return wait_list.empty();
+ }
+
+ KThread::WaiterList::iterator begin() {
+ return wait_list.begin();
+ }
+ KThread::WaiterList::iterator end() {
+ return wait_list.end();
+ }
+
+ bool SleepThread(KThread* t) {
+ KScopedSchedulerLock sl{kernel};
+
+ // If the thread needs terminating, don't enqueue it.
+ if (t->IsTerminationRequested()) {
+ return false;
+ }
+
+ // Set the thread's queue and mark it as waiting.
+ t->SetSleepingQueue(this);
+ t->SetState(ThreadState::Waiting);
+
+ // Add the thread to the queue.
+ wait_list.push_back(*t);
+
+ return true;
+ }
+
+ void WakeupThread(KThread* t) {
+ KScopedSchedulerLock sl{kernel};
+
+ // Remove the thread from the queue.
+ wait_list.erase(wait_list.iterator_to(*t));
+
+ // Mark the thread as no longer sleeping.
+ t->SetState(ThreadState::Runnable);
+ t->SetSleepingQueue(nullptr);
+ }
+
+ KThread* WakeupFrontThread() {
+ KScopedSchedulerLock sl{kernel};
+
+ if (wait_list.empty()) {
+ return nullptr;
+ } else {
+ // Remove the thread from the queue.
+ auto it = wait_list.begin();
+ KThread* thread = std::addressof(*it);
+ wait_list.erase(it);
+
+ ASSERT(thread->GetState() == ThreadState::Waiting);
+
+ // Mark the thread as no longer sleeping.
+ thread->SetState(ThreadState::Runnable);
+ thread->SetSleepingQueue(nullptr);
+
+ return thread;
+ }
+ }
+
+private:
+ KernelCore& kernel;
+ KThread::WaiterList wait_list{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp
new file mode 100644
index 000000000..25c52edb2
--- /dev/null
+++ b/src/core/hle/kernel/k_writable_event.cpp
@@ -0,0 +1,27 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
+
+namespace Kernel {
+
+KWritableEvent::KWritableEvent(KernelCore& kernel, std::string&& name)
+ : Object{kernel, std::move(name)} {}
+KWritableEvent::~KWritableEvent() = default;
+
+void KWritableEvent::Initialize(KEvent* parent_) {
+ parent = parent_;
+}
+
+ResultCode KWritableEvent::Signal() {
+ return parent->GetReadableEvent()->Signal();
+}
+
+ResultCode KWritableEvent::Clear() {
+ return parent->GetReadableEvent()->Clear();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h
new file mode 100644
index 000000000..518f5448d
--- /dev/null
+++ b/src/core/hle/kernel/k_writable_event.h
@@ -0,0 +1,44 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/object.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KEvent;
+
+class KWritableEvent final : public Object {
+public:
+ explicit KWritableEvent(KernelCore& kernel, std::string&& name);
+ ~KWritableEvent() override;
+
+ std::string GetTypeName() const override {
+ return "KWritableEvent";
+ }
+
+ static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
+ HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
+ void Initialize(KEvent* parent_);
+
+ void Finalize() override {}
+
+ ResultCode Signal();
+ ResultCode Clear();
+
+ KEvent* GetParent() const {
+ return parent;
+ }
+
+private:
+ KEvent* parent{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e8ece8164..b6e6f115e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -26,20 +26,19 @@
#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/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_thread.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/service_thread.h"
#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/kernel/synchronization.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -51,22 +50,23 @@ namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system, KernelCore& kernel)
- : synchronization{system}, time_manager{system}, global_handle_table{kernel}, system{
- system} {}
+ : time_manager{system}, global_handle_table{kernel}, system{system} {}
void SetMulticore(bool is_multicore) {
this->is_multicore = is_multicore;
}
void Initialize(KernelCore& kernel) {
+ global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
+
RegisterHostThread();
- global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
service_thread_manager =
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
+ is_phantom_mode_for_singlecore = false;
InitializePhysicalCores();
- InitializeSystemResourceLimit(kernel);
+ InitializeSystemResourceLimit(kernel, system);
InitializeMemoryLayout();
InitializePreemption(kernel);
InitializeSchedulers();
@@ -118,34 +118,40 @@ struct KernelCore::Impl {
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++) {
+ for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
cores.emplace_back(i, system, *schedulers[i], interrupts);
}
}
void InitializeSchedulers() {
- for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
cores[i].Scheduler().Initialize();
}
}
// Creates the default system resource limit
- void InitializeSystemResourceLimit(KernelCore& kernel) {
- system_resource_limit = ResourceLimit::Create(kernel);
+ void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system) {
+ system_resource_limit = std::make_shared<KResourceLimit>(kernel, system);
// If setting the default system values fails, then something seriously wrong has occurred.
- ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x100000000)
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, 0x100000000)
+ .IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
.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());
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 933).IsSuccess());
- if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) ||
- !system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) {
+ // Derived from recent software updates. The kernel reserves 27MB
+ constexpr u64 kernel_size{0x1b00000};
+ if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size)) {
UNREACHABLE();
}
+ // Reserve secure applet memory, introduced in firmware 5.0.0
+ constexpr u64 secure_applet_memory_size{0x400000};
+ ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
+ secure_applet_memory_size));
}
void InitializePreemption(KernelCore& kernel) {
@@ -170,11 +176,9 @@ struct KernelCore::Impl {
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);
+ auto thread_res = KThread::Create(system, ThreadType::HighPriority, 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();
}
@@ -209,6 +213,17 @@ struct KernelCore::Impl {
return host_thread_id;
}
+ // Gets the dummy KThread for the caller, allocating a new one if this is the first time
+ KThread* GetHostDummyThread() {
+ const thread_local auto thread =
+ KThread::Create(
+ system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
+ KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
+ []([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
+ .Unwrap();
+ return thread.get();
+ }
+
/// Registers a CPU core thread by allocating a host thread ID for it
void RegisterCoreThread(std::size_t core_id) {
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
@@ -221,6 +236,7 @@ struct KernelCore::Impl {
/// Registers a new host thread by allocating a host thread ID for it
void RegisterHostThread() {
[[maybe_unused]] const auto this_id = GetHostThreadId();
+ [[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
}
[[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -231,20 +247,21 @@ struct KernelCore::Impl {
return this_id;
}
- [[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
- Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
- result.host_handle = GetCurrentHostThreadID();
- if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
- return result;
- }
- const Kernel::KScheduler& sched = cores[result.host_handle].Scheduler();
- const Kernel::Thread* current = sched.GetCurrentThread();
- if (current != nullptr && !current->IsPhantomMode()) {
- result.guest_handle = current->GetGlobalHandle();
- } else {
- result.guest_handle = InvalidHandle;
+ bool IsPhantomModeForSingleCore() const {
+ return is_phantom_mode_for_singlecore;
+ }
+
+ void SetIsPhantomModeForSingleCore(bool value) {
+ ASSERT(!is_multicore);
+ is_phantom_mode_for_singlecore = value;
+ }
+
+ KThread* GetCurrentEmuThread() {
+ const auto thread_id = GetCurrentHostThreadID();
+ if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
+ return GetHostDummyThread();
}
- return result;
+ return schedulers[thread_id]->GetCurrentThread();
}
void InitializeMemoryLayout() {
@@ -291,8 +308,11 @@ struct KernelCore::Impl {
// Allocate slab heaps
user_slab_heap_pages = std::make_unique<Memory::SlabHeap<Memory::Page>>();
+ constexpr u64 user_slab_heap_size{0x1ef000};
+ // Reserve slab heaps
+ ASSERT(
+ system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
// 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);
@@ -307,10 +327,9 @@ struct KernelCore::Impl {
std::vector<std::shared_ptr<Process>> process_list;
Process* current_process = nullptr;
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
- Kernel::Synchronization synchronization;
Kernel::TimeManager time_manager;
- std::shared_ptr<ResourceLimit> system_resource_limit;
+ std::shared_ptr<KResourceLimit> system_resource_limit;
std::shared_ptr<Core::Timing::EventType> preemption_event;
@@ -345,11 +364,12 @@ struct KernelCore::Impl {
// the release of itself
std::unique_ptr<Common::ThreadWorker> service_thread_manager;
- std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
+ std::array<std::shared_ptr<KThread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};
+ bool is_phantom_mode_for_singlecore{};
u32 single_core_thread_id{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
@@ -379,12 +399,12 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
-std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
+std::shared_ptr<KResourceLimit> KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
-std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
- return impl->global_handle_table.Get<Thread>(handle);
+std::shared_ptr<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
+ return impl->global_handle_table.Get<KThread>(handle);
}
void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
@@ -461,14 +481,6 @@ const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Kern
return impl->interrupts;
}
-Kernel::Synchronization& KernelCore::Synchronization() {
- return impl->synchronization;
-}
-
-const Kernel::Synchronization& KernelCore::Synchronization() const {
- return impl->synchronization;
-}
-
Kernel::TimeManager& KernelCore::TimeManager() {
return impl->time_manager;
}
@@ -557,8 +569,8 @@ u32 KernelCore::GetCurrentHostThreadID() const {
return impl->GetCurrentHostThreadID();
}
-Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
- return impl->GetCurrentEmuThreadID();
+KThread* KernelCore::GetCurrentEmuThread() const {
+ return impl->GetCurrentEmuThread();
}
Memory::MemoryManager& KernelCore::MemoryManager() {
@@ -613,9 +625,11 @@ void KernelCore::Suspend(bool in_suspention) {
const bool should_suspend = exception_exited || in_suspention;
{
KScopedSchedulerLock lock(*this);
- ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep;
+ const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting;
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- impl->suspend_threads[i]->SetStatus(status);
+ impl->suspend_threads[i]->SetState(state);
+ impl->suspend_threads[i]->SetWaitReasonForDebugging(
+ ThreadWaitReasonForDebugging::Suspended);
}
}
}
@@ -654,4 +668,12 @@ void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> servi
});
}
+bool KernelCore::IsPhantomModeForSingleCore() const {
+ return impl->IsPhantomModeForSingleCore();
+}
+
+void KernelCore::SetIsPhantomModeForSingleCore(bool value) {
+ impl->SetIsPhantomModeForSingleCore(value);
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index e3169f5a7..806a0d986 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -33,20 +33,23 @@ template <typename T>
class SlabHeap;
} // namespace Memory
-class AddressArbiter;
class ClientPort;
class GlobalSchedulerContext;
class HandleTable;
class PhysicalCore;
class Process;
-class ResourceLimit;
+class KResourceLimit;
class KScheduler;
class SharedMemory;
class ServiceThread;
class Synchronization;
-class Thread;
+class KThread;
class TimeManager;
+using EmuThreadHandle = uintptr_t;
+constexpr EmuThreadHandle EmuThreadHandleInvalid{};
+constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
+
/// Represents a single instance of the kernel.
class KernelCore {
private:
@@ -82,10 +85,10 @@ public:
void Shutdown();
/// Retrieves a shared pointer to the system resource limit instance.
- std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
+ std::shared_ptr<KResourceLimit> GetSystemResourceLimit() const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
- std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
+ std::shared_ptr<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(std::shared_ptr<Process> process);
@@ -129,12 +132,6 @@ public:
/// 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();
-
- /// Gets the an instance of the Synchronization Interface.
- const Kernel::Synchronization& Synchronization() const;
-
/// Gets the an instance of the TimeManager Interface.
Kernel::TimeManager& TimeManager();
@@ -168,8 +165,8 @@ public:
/// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
- /// Gets the current host_thread/guest_thread handle.
- Core::EmuThreadHandle GetCurrentEmuThreadID() const;
+ /// Gets the current host_thread/guest_thread pointer.
+ KThread* GetCurrentEmuThread() const;
/// Gets the current host_thread handle.
u32 GetCurrentHostThreadID() const;
@@ -244,10 +241,14 @@ public:
*/
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
+ /// Workaround for single-core mode when preempting threads while idle.
+ bool IsPhantomModeForSingleCore() const;
+ void SetIsPhantomModeForSingleCore(bool value);
+
private:
friend class Object;
friend class Process;
- friend class Thread;
+ friend class KThread;
/// Creates a new object ID, incrementing the internal object ID counter.
u32 CreateNewObjectID();
diff --git a/src/core/hle/kernel/memory/address_space_info.cpp b/src/core/hle/kernel/memory/address_space_info.cpp
index e4288cab4..6cf43ba24 100644
--- a/src/core/hle/kernel/memory/address_space_info.cpp
+++ b/src/core/hle/kernel/memory/address_space_info.cpp
@@ -96,6 +96,7 @@ u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
}
UNREACHABLE();
+ return 0;
}
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
@@ -112,6 +113,7 @@ std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
}
UNREACHABLE();
+ return 0;
}
} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h
index 9b3d6267a..c7c0b2f49 100644
--- a/src/core/hle/kernel/memory/memory_layout.h
+++ b/src/core/hle/kernel/memory/memory_layout.h
@@ -5,9 +5,28 @@
#pragma once
#include "common/common_types.h"
+#include "core/device_memory.h"
namespace Kernel::Memory {
+constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024;
+constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
+constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
+constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth;
+constexpr std::size_t KernelVirtualAddressSpaceEnd =
+ KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
+constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1;
+constexpr std::size_t KernelVirtualAddressSpaceSize =
+ KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
+
+constexpr bool IsKernelAddressKey(VAddr key) {
+ return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
+}
+
+constexpr bool IsKernelAddress(VAddr address) {
+ return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
+}
+
class MemoryRegion final {
friend class MemoryLayout;
diff --git a/src/core/hle/kernel/memory/memory_manager.cpp b/src/core/hle/kernel/memory/memory_manager.cpp
index acf13585c..77f135cdc 100644
--- a/src/core/hle/kernel/memory/memory_manager.cpp
+++ b/src/core/hle/kernel/memory/memory_manager.cpp
@@ -8,9 +8,9 @@
#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"
+#include "core/hle/kernel/svc_results.h"
namespace Kernel::Memory {
@@ -95,7 +95,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
// 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;
+ return ResultOutOfMemory;
}
// TODO (bunnei): Support multiple managers
@@ -140,7 +140,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
// Only succeed if we allocated as many pages as we wanted
if (num_pages) {
- return ERR_OUT_OF_MEMORY;
+ return ResultOutOfMemory;
}
// We succeeded!
diff --git a/src/core/hle/kernel/memory/page_heap.h b/src/core/hle/kernel/memory/page_heap.h
index 22b0de860..131093284 100644
--- a/src/core/hle/kernel/memory/page_heap.h
+++ b/src/core/hle/kernel/memory/page_heap.h
@@ -8,11 +8,11 @@
#pragma once
#include <array>
+#include <bit>
#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"
@@ -105,7 +105,7 @@ private:
ASSERT(depth == 0);
return -1;
}
- offset = offset * 64 + Common::CountTrailingZeroes64(v);
+ offset = offset * 64 + static_cast<u32>(std::countr_zero(v));
++depth;
} while (depth < static_cast<s32>(used_depths));
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index 080886554..00ed9b881 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -6,7 +6,7 @@
#include "common/assert.h"
#include "common/scope_exit.h"
#include "core/core.h"
-#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/address_space_info.h"
#include "core/hle/kernel/memory/memory_block.h"
@@ -15,7 +15,7 @@
#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/hle/kernel/svc_results.h"
#include "core/memory.h"
namespace Kernel::Memory {
@@ -141,7 +141,7 @@ ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_t
(alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
if (alloc_size < needed_size) {
UNREACHABLE();
- return ERR_OUT_OF_MEMORY;
+ return ResultOutOfMemory;
}
const std::size_t remaining_size{alloc_size - needed_size};
@@ -277,11 +277,11 @@ ResultCode PageTable::MapProcessCode(VAddr addr, std::size_t num_pages, MemorySt
const u64 size{num_pages * PageSize};
if (!CanContain(addr, size, state)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (IsRegionMapped(addr, size)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
PageLinkedList page_linked_list;
@@ -307,7 +307,7 @@ ResultCode PageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::
MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
if (IsRegionMapped(dst_addr, size)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
PageLinkedList page_linked_list;
@@ -409,27 +409,25 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t 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;
+ // Reserve the memory from the process resource limit.
+ KScopedResourceReservation memory_reservation(
+ system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
+ remaining_size);
+ if (!memory_reservation.Succeeded()) {
+ LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
+ return ResultResourceLimitedExceeded;
}
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));
+ CASCADE_CODE(
+ system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, memory_pool));
- block_guard.Cancel();
- }
+ // We succeeded, so commit the memory reservation.
+ memory_reservation.Commit();
MapPhysicalMemory(page_linked_list, addr, end_addr);
@@ -454,12 +452,12 @@ ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) {
if (info.state == MemoryState::Normal) {
if (info.attribute != MemoryAttribute::None) {
- result = ERR_INVALID_ADDRESS_STATE;
+ result = ResultInvalidCurrentMemory;
return;
}
mapped_size += GetSizeInRange(info, addr, end_addr);
} else if (info.state != MemoryState::Free) {
- result = ERR_INVALID_ADDRESS_STATE;
+ result = ResultInvalidCurrentMemory;
}
});
@@ -474,7 +472,7 @@ ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
CASCADE_CODE(UnmapMemory(addr, size));
auto process{system.Kernel().CurrentProcess()};
- process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, mapped_size);
+ process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
physical_memory_usage -= mapped_size;
return RESULT_SUCCESS;
@@ -526,7 +524,7 @@ ResultCode PageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
MemoryAttribute::Mask, MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped));
if (IsRegionMapped(dst_addr, size)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
PageLinkedList page_linked_list;
@@ -577,7 +575,7 @@ ResultCode PageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
AddRegionToPages(dst_addr, num_pages, dst_pages);
if (!dst_pages.IsEqual(src_pages)) {
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
{
@@ -626,11 +624,11 @@ ResultCode PageTable::MapPages(VAddr addr, PageLinkedList& page_linked_list, Mem
const std::size_t size{num_pages * PageSize};
if (!CanContain(addr, size, state)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (IsRegionMapped(addr, num_pages * PageSize)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
CASCADE_CODE(MapPages(addr, page_linked_list, perm));
@@ -768,7 +766,7 @@ ResultCode PageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
if (size > heap_region_end - heap_region_start) {
- return ERR_OUT_OF_MEMORY;
+ return ResultOutOfMemory;
}
const u64 previous_heap_size{GetHeapSize()};
@@ -781,10 +779,14 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
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;
+ // Reserve memory for the heap extension.
+ KScopedResourceReservation memory_reservation(
+ system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
+ delta);
+
+ if (!memory_reservation.Succeeded()) {
+ LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
+ return ResultResourceLimitedExceeded;
}
PageLinkedList page_linked_list;
@@ -794,12 +796,15 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
if (IsRegionMapped(current_heap_addr, delta)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
CASCADE_CODE(
Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
+ // Succeeded in allocation, commit the resource reservation
+ memory_reservation.Commit();
+
block_manager->Update(current_heap_addr, num_pages, MemoryState::Normal,
MemoryPermission::ReadAndWrite);
@@ -816,17 +821,17 @@ ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, s
std::lock_guard lock{page_table_lock};
if (!CanContain(region_start, region_num_pages * PageSize, state)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (region_num_pages <= needed_num_pages) {
- return ERR_OUT_OF_MEMORY;
+ return ResultOutOfMemory;
}
const VAddr addr{
AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
if (!addr) {
- return ERR_OUT_OF_MEMORY;
+ return ResultOutOfMemory;
}
if (is_map_only) {
@@ -1105,13 +1110,13 @@ constexpr ResultCode PageTable::CheckMemoryState(const MemoryInfo& info, MemoryS
MemoryAttribute attr) const {
// Validate the states match expectation
if ((info.state & state_mask) != state) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if ((info.perm & perm_mask) != perm) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if ((info.attribute & attr_mask) != attr) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
return RESULT_SUCCESS;
@@ -1138,14 +1143,14 @@ ResultCode PageTable::CheckMemoryState(MemoryState* out_state, MemoryPermission*
while (true) {
// Validate the current block
if (!(info.state == first_state)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (!(info.perm == first_perm)) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (!((info.attribute | static_cast<MemoryAttribute>(ignore_attr)) ==
(first_attr | static_cast<MemoryAttribute>(ignore_attr)))) {
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
// Validate against the provided masks
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
deleted file mode 100644
index 4f8075e0e..000000000
--- a/src/core/hle/kernel/mutex.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <memory>
-#include <utility>
-#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"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/mutex.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/result.h"
-#include "core/memory.h"
-
-namespace Kernel {
-
-/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
-/// those.
-static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
- const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) {
-
- std::shared_ptr<Thread> highest_priority_thread;
- u32 num_waiters = 0;
-
- for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
- if (thread->GetMutexWaitAddress() != mutex_addr)
- continue;
-
- ++num_waiters;
- if (highest_priority_thread == nullptr ||
- thread->GetPriority() < highest_priority_thread->GetPriority()) {
- highest_priority_thread = thread;
- }
- }
-
- return {highest_priority_thread, num_waiters};
-}
-
-/// 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)
- continue;
-
- ASSERT(thread->GetLockOwner() == current_thread.get());
- current_thread->RemoveMutexWaiter(thread);
- if (new_owner != thread)
- new_owner->AddMutexWaiter(thread);
- }
-}
-
-Mutex::Mutex(Core::System& system) : system{system} {}
-Mutex::~Mutex() = default;
-
-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;
- }
-
- auto& kernel = system.Kernel();
- std::shared_ptr<Thread> current_thread =
- SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
- {
- KScopedSchedulerLock lock(kernel);
- // The mutex address must be 4-byte aligned
- if ((address % sizeof(u32)) != 0) {
- return ERR_INVALID_ADDRESS;
- }
-
- 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);
-
- // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of
- // another thread.
- ASSERT(requesting_thread == current_thread);
-
- current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
-
- 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;
- }
-
- if (holding_thread == nullptr) {
- return ERR_INVALID_HANDLE;
- }
-
- // Wait until the mutex is released
- current_thread->SetMutexWaitAddress(address);
- current_thread->SetWaitHandle(requesting_thread_handle);
-
- current_thread->SetStatus(ThreadStatus::WaitMutex);
-
- // Update the lock holder thread's priority to prevent priority inversion.
- holding_thread->AddMutexWaiter(current_thread);
- }
-
- {
- KScopedSchedulerLock lock(kernel);
- auto* owner = current_thread->GetLockOwner();
- if (owner != nullptr) {
- owner->RemoveMutexWaiter(current_thread);
- }
- }
- return current_thread->GetSignalingResult();
-}
-
-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) {
- LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
- return {ERR_INVALID_ADDRESS, nullptr};
- }
-
- auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address);
- if (new_owner == nullptr) {
- system.Memory().Write32(address, 0);
- return {RESULT_SUCCESS, nullptr};
- }
- // Transfer the ownership of the mutex from the previous owner to the new one.
- 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();
-
- system.Memory().Write32(address, mutex_value);
- return {RESULT_SUCCESS, new_owner};
-}
-
-ResultCode Mutex::Release(VAddr address) {
- auto& kernel = system.Kernel();
- KScopedSchedulerLock lock(kernel);
-
- std::shared_ptr<Thread> current_thread =
- SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
-
- auto [result, new_owner] = Unlock(current_thread, address);
-
- 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
deleted file mode 100644
index 3b81dc3df..000000000
--- a/src/core/hle/kernel/mutex.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-union ResultCode;
-
-namespace Core {
-class System;
-}
-
-namespace Kernel {
-
-class Mutex final {
-public:
- explicit Mutex(Core::System& system);
- ~Mutex();
-
- /// Flag that indicates that a mutex still has threads waiting for it.
- static constexpr u32 MutexHasWaitersFlag = 0x40000000;
- /// Mask of the bits in a mutex address value that contain the mutex owner.
- static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
-
- /// Attempts to acquire a mutex at the specified address.
- 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);
-
-private:
- Core::System& system;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index 2c571792b..d7f40c403 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -8,7 +8,10 @@
namespace Kernel {
-Object::Object(KernelCore& kernel) : kernel{kernel}, object_id{kernel.CreateNewObjectID()} {}
+Object::Object(KernelCore& kernel_)
+ : kernel{kernel_}, object_id{kernel_.CreateNewObjectID()}, name{"[UNKNOWN KERNEL OBJECT]"} {}
+Object::Object(KernelCore& kernel_, std::string&& name_)
+ : kernel{kernel_}, object_id{kernel_.CreateNewObjectID()}, name{std::move(name_)} {}
Object::~Object() = default;
bool Object::IsWaitable() const {
@@ -21,6 +24,7 @@ bool Object::IsWaitable() const {
return true;
case HandleType::Unknown:
+ case HandleType::Event:
case HandleType::WritableEvent:
case HandleType::SharedMemory:
case HandleType::TransferMemory:
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index e3391e2af..501e58b33 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -18,6 +18,7 @@ using Handle = u32;
enum class HandleType : u32 {
Unknown,
+ Event,
WritableEvent,
ReadableEvent,
SharedMemory,
@@ -34,7 +35,8 @@ enum class HandleType : u32 {
class Object : NonCopyable, public std::enable_shared_from_this<Object> {
public:
- explicit Object(KernelCore& kernel);
+ explicit Object(KernelCore& kernel_);
+ explicit Object(KernelCore& kernel_, std::string&& name_);
virtual ~Object();
/// Returns a unique identifier for the object. For debugging purposes only.
@@ -46,22 +48,30 @@ public:
return "[BAD KERNEL OBJECT TYPE]";
}
virtual std::string GetName() const {
- return "[UNKNOWN KERNEL OBJECT]";
+ return name;
}
virtual HandleType GetHandleType() const = 0;
+ void Close() {
+ // TODO(bunnei): This is a placeholder to decrement the reference count, which we will use
+ // when we implement KAutoObject instead of using shared_ptr.
+ }
+
/**
* Check if a thread can wait on the object
* @return True if a thread can wait on the object, otherwise false
*/
bool IsWaitable() const;
+ virtual void Finalize() = 0;
+
protected:
/// The kernel instance this object was created under.
KernelCore& kernel;
private:
std::atomic<u32> object_id{0};
+ std::string name;
};
template <typename T>
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index b905b486a..47b3ac57b 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -14,15 +14,16 @@
#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/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_thread.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/thread.h"
+#include "core/hle/kernel/svc_results.h"
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/settings.h"
@@ -38,11 +39,11 @@ namespace {
*/
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);
+ ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
+ auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
+ owner_process.GetIdealCoreId(), stack_top, &owner_process);
- std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
+ std::shared_ptr<KThread> 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();
@@ -55,7 +56,7 @@ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority,
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
{
KScopedSchedulerLock lock{kernel};
- thread->SetStatus(ThreadStatus::Ready);
+ thread->SetState(ThreadState::Runnable);
}
}
} // Anonymous namespace
@@ -117,7 +118,10 @@ 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 = ResourceLimit::Create(kernel);
+
+ // TODO: This is inaccurate
+ // The process should hold a reference to the kernel-wide resource limit.
+ process->resource_limit = std::make_shared<KResourceLimit>(kernel, system);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
@@ -133,12 +137,32 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
return process;
}
-std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
+std::shared_ptr<KResourceLimit> Process::GetResourceLimit() const {
return resource_limit;
}
+void Process::IncrementThreadCount() {
+ ASSERT(num_threads >= 0);
+ num_created_threads++;
+
+ if (const auto count = ++num_threads; count > peak_num_threads) {
+ peak_num_threads = count;
+ }
+}
+
+void Process::DecrementThreadCount() {
+ ASSERT(num_threads > 0);
+
+ if (const auto count = --num_threads; count == 0) {
+ UNIMPLEMENTED_MSG("Process termination is not implemented!");
+ }
+}
+
u64 Process::GetTotalPhysicalMemoryAvailable() const {
- const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) +
+ // TODO: This is expected to always return the application memory pool size after accurately
+ // reserving kernel resources. The current workaround uses a process-local resource limit of
+ // application memory pool size, which is inaccurate.
+ const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
main_thread_stack_size};
@@ -162,68 +186,79 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
}
-void Process::InsertConditionVariableThread(std::shared_ptr<Thread> thread) {
- VAddr cond_var_addr = thread->GetCondVarWaitAddress();
- std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
- auto it = thread_list.begin();
- while (it != thread_list.end()) {
- const std::shared_ptr<Thread> current_thread = *it;
- if (current_thread->GetPriority() > thread->GetPriority()) {
- thread_list.insert(it, thread);
- return;
+bool Process::ReleaseUserException(KThread* thread) {
+ KScopedSchedulerLock sl{kernel};
+
+ if (exception_thread == thread) {
+ exception_thread = nullptr;
+
+ // Remove waiter thread.
+ s32 num_waiters{};
+ KThread* next = thread->RemoveWaiterByKey(
+ std::addressof(num_waiters),
+ reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
+ if (next != nullptr) {
+ if (next->GetState() == ThreadState::Waiting) {
+ next->SetState(ThreadState::Runnable);
+ } else {
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
+ }
}
- ++it;
+
+ return true;
+ } else {
+ return false;
}
- thread_list.push_back(thread);
}
-void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
- VAddr cond_var_addr = thread->GetCondVarWaitAddress();
- std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
- auto it = thread_list.begin();
- while (it != thread_list.end()) {
- const std::shared_ptr<Thread> current_thread = *it;
- if (current_thread.get() == thread.get()) {
- thread_list.erase(it);
- return;
- }
- ++it;
- }
+void Process::PinCurrentThread() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Get the current thread.
+ const s32 core_id = GetCurrentCoreId(kernel);
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
+
+ // Pin it.
+ PinThread(core_id, cur_thread);
+ cur_thread->Pin();
+
+ // An update is needed.
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
-std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(
- const VAddr cond_var_addr) {
- std::vector<std::shared_ptr<Thread>> result{};
- std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
- auto it = thread_list.begin();
- while (it != thread_list.end()) {
- std::shared_ptr<Thread> current_thread = *it;
- result.push_back(current_thread);
- ++it;
- }
- return result;
+void Process::UnpinCurrentThread() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Get the current thread.
+ const s32 core_id = GetCurrentCoreId(kernel);
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
+
+ // Unpin it.
+ cur_thread->Unpin();
+ UnpinThread(core_id, cur_thread);
+
+ // An update is needed.
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
-void Process::RegisterThread(const Thread* thread) {
+void Process::RegisterThread(const KThread* thread) {
thread_list.push_back(thread);
}
-void Process::UnregisterThread(const Thread* thread) {
+void Process::UnregisterThread(const KThread* thread) {
thread_list.remove(thread);
}
-ResultCode Process::ClearSignalState() {
- KScopedSchedulerLock lock(system.Kernel());
- if (status == ProcessStatus::Exited) {
- LOG_ERROR(Kernel, "called on a terminated process instance.");
- return ERR_INVALID_STATE;
- }
+ResultCode Process::Reset() {
+ // Lock the process and the scheduler.
+ KScopedLightLock lk(state_lock);
+ KScopedSchedulerLock sl{kernel};
- if (!is_signaled) {
- LOG_ERROR(Kernel, "called on a process instance that isn't signaled.");
- return ERR_INVALID_STATE;
- }
+ // Validate that we're in a state that we can reset.
+ R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState);
+ R_UNLESS(is_signaled, ResultInvalidState);
+ // Clear signaled.
is_signaled = false;
return RESULT_SUCCESS;
}
@@ -236,6 +271,17 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
system_resource_size = metadata.GetSystemResourceSize();
image_size = code_size;
+ // Set initial resource limits
+ resource_limit->SetLimitValue(
+ LimitableResource::PhysicalMemory,
+ kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
+ KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
+ code_size + system_resource_size);
+ if (!memory_reservation.Succeeded()) {
+ LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
+ code_size + system_resource_size);
+ return ResultResourceLimitedExceeded;
+ }
// Initialize proces address space
if (const ResultCode result{
page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000,
@@ -277,24 +323,22 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
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));
+ resource_limit->SetLimitValue(LimitableResource::Threads, 608);
+ resource_limit->SetLimitValue(LimitableResource::Events, 700);
+ resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128);
+ resource_limit->SetLimitValue(LimitableResource::Sessions, 894);
// Create TLS region
tls_region_address = CreateTLSRegion();
+ memory_reservation.Commit();
return handle_table.SetSize(capabilities.GetHandleTableSize());
}
void Process::Run(s32 main_thread_priority, u64 stack_size) {
AllocateMainThreadStack(stack_size);
+ resource_limit->Reserve(LimitableResource::Threads, 1);
+ resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
@@ -302,14 +346,12 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
ChangeStatus(ProcessStatus::Running);
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() {
ChangeStatus(ProcessStatus::Exiting);
- const auto stop_threads = [this](const std::vector<std::shared_ptr<Thread>>& thread_list) {
+ const auto stop_threads = [this](const std::vector<std::shared_ptr<KThread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->GetOwnerProcess() != this)
continue;
@@ -318,10 +360,10 @@ void Process::PrepareForTermination() {
continue;
// TODO(Subv): When are the other running/ready threads terminated?
- ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch,
+ ASSERT_MSG(thread->GetState() == ThreadState::Waiting,
"Exiting processes with non-waiting threads is currently unimplemented");
- thread->Stop();
+ thread->Exit();
}
};
@@ -330,6 +372,11 @@ void Process::PrepareForTermination() {
FreeTLSRegion(tls_region_address);
tls_region_address = 0;
+ if (resource_limit) {
+ resource_limit->Release(LimitableResource::PhysicalMemory,
+ main_thread_stack_size + image_size);
+ }
+
ChangeStatus(ProcessStatus::Exited);
}
@@ -406,21 +453,18 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite);
}
+bool Process::IsSignaled() const {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ return is_signaled;
+}
+
Process::Process(Core::System& system)
- : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>(
- system)},
- handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
+ : KSynchronizationObject{system.Kernel()},
+ page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
+ address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {}
Process::~Process() = default;
-void Process::Acquire(Thread* thread) {
- ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
-}
-
-bool Process::ShouldWait(const Thread* thread) const {
- return !is_signaled;
-}
-
void Process::ChangeStatus(ProcessStatus new_status) {
if (status == new_status) {
return;
@@ -428,7 +472,7 @@ void Process::ChangeStatus(ProcessStatus new_status) {
status = new_status;
is_signaled = true;
- Signal();
+ NotifyAvailable();
}
ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index e412e58aa..320b0f347 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -11,11 +11,11 @@
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
-#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/handle_table.h"
-#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/k_address_arbiter.h"
+#include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/process_capability.h"
-#include "core/hle/kernel/synchronization_object.h"
#include "core/hle/result.h"
namespace Core {
@@ -29,8 +29,8 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
-class ResourceLimit;
-class Thread;
+class KResourceLimit;
+class KThread;
class TLSPage;
struct CodeSet;
@@ -63,7 +63,7 @@ enum class ProcessStatus {
DebugBreak,
};
-class Process final : public SynchronizationObject {
+class Process final : public KSynchronizationObject {
public:
explicit Process(Core::System& system);
~Process() override;
@@ -123,24 +123,30 @@ public:
return handle_table;
}
- /// Gets a reference to the process' address arbiter.
- AddressArbiter& GetAddressArbiter() {
- return address_arbiter;
+ ResultCode SignalToAddress(VAddr address) {
+ return condition_var.SignalToAddress(address);
}
- /// Gets a const reference to the process' address arbiter.
- const AddressArbiter& GetAddressArbiter() const {
- return address_arbiter;
+ ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) {
+ return condition_var.WaitForAddress(handle, address, tag);
}
- /// Gets a reference to the process' mutex lock.
- Mutex& GetMutex() {
- return mutex;
+ void SignalConditionVariable(u64 cv_key, int32_t count) {
+ return condition_var.Signal(cv_key, count);
}
- /// Gets a const reference to the process' mutex lock
- const Mutex& GetMutex() const {
- return mutex;
+ ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
+ return condition_var.Wait(address, cv_key, tag, ns);
+ }
+
+ ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value,
+ s32 count) {
+ return address_arbiter.SignalToAddress(address, signal_type, value, count);
+ }
+
+ ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
+ s64 timeout) {
+ return address_arbiter.WaitForAddress(address, arb_type, value, timeout);
}
/// Gets the address to the process' dedicated TLS region.
@@ -164,13 +170,18 @@ public:
}
/// Gets the resource limit descriptor for this process
- std::shared_ptr<ResourceLimit> GetResourceLimit() const;
+ std::shared_ptr<KResourceLimit> GetResourceLimit() const;
/// Gets the ideal CPU core ID for this process
- u8 GetIdealCore() const {
+ u8 GetIdealCoreId() const {
return ideal_core;
}
+ /// Checks if the specified thread priority is valid.
+ bool CheckThreadPriority(s32 prio) const {
+ return ((1ULL << prio) & GetPriorityMask()) != 0;
+ }
+
/// Gets the bitmask of allowed cores that this process' threads can run on.
u64 GetCoreMask() const {
return capabilities.GetCoreMask();
@@ -206,6 +217,14 @@ public:
return is_64bit_process;
}
+ [[nodiscard]] bool IsSuspended() const {
+ return is_suspended;
+ }
+
+ void SetSuspended(bool suspended) {
+ is_suspended = suspended;
+ }
+
/// Gets the total running time of the process instance in ticks.
u64 GetCPUTimeTicks() const {
return total_process_running_time_ticks;
@@ -226,6 +245,33 @@ public:
++schedule_count;
}
+ void IncrementThreadCount();
+ void DecrementThreadCount();
+
+ void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
+ running_threads[core] = thread;
+ running_thread_idle_counts[core] = idle_count;
+ }
+
+ void ClearRunningThread(KThread* thread) {
+ for (size_t i = 0; i < running_threads.size(); ++i) {
+ if (running_threads[i] == thread) {
+ running_threads[i] = nullptr;
+ }
+ }
+ }
+
+ [[nodiscard]] KThread* GetRunningThread(s32 core) const {
+ return running_threads[core];
+ }
+
+ bool ReleaseUserException(KThread* thread);
+
+ [[nodiscard]] KThread* GetPinnedThread(s32 core_id) const {
+ ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
+ return pinned_threads[core_id];
+ }
+
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
u64 GetRandomEntropy(std::size_t index) const {
return random_entropy.at(index);
@@ -246,26 +292,17 @@ public:
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner.
- const std::list<const Thread*>& GetThreadList() const {
+ const std::list<const KThread*>& GetThreadList() const {
return thread_list;
}
- /// Insert a thread into the condition variable wait container
- void InsertConditionVariableThread(std::shared_ptr<Thread> thread);
-
- /// Remove a thread from the condition variable wait container
- void RemoveConditionVariableThread(std::shared_ptr<Thread> thread);
-
- /// Obtain all condition variable threads waiting for some address
- std::vector<std::shared_ptr<Thread>> GetConditionVariableThreads(VAddr cond_var_addr);
-
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
- void RegisterThread(const Thread* thread);
+ void RegisterThread(const KThread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
- void UnregisterThread(const Thread* thread);
+ void UnregisterThread(const KThread* thread);
/// Clears the signaled state of the process if and only if it's signaled.
///
@@ -275,7 +312,7 @@ public:
/// @pre The process must be in a signaled state. If this is called on a
/// process instance that is not signaled, ERR_INVALID_STATE will be
/// returned.
- ResultCode ClearSignalState();
+ ResultCode Reset();
/**
* Loads process-specifics configuration info with metadata provided
@@ -304,6 +341,17 @@ public:
void LoadModule(CodeSet code_set, VAddr base_addr);
+ bool IsSignaled() const override;
+
+ void Finalize() override {}
+
+ void PinCurrentThread();
+ void UnpinCurrentThread();
+
+ KLightLock& GetStateLock() {
+ return state_lock;
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management
@@ -314,11 +362,19 @@ public:
void FreeTLSRegion(VAddr tls_address);
private:
- /// Checks if the specified thread should wait until this process is available.
- bool ShouldWait(const Thread* thread) const override;
+ void PinThread(s32 core_id, KThread* thread) {
+ ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
+ ASSERT(thread != nullptr);
+ ASSERT(pinned_threads[core_id] == nullptr);
+ pinned_threads[core_id] = thread;
+ }
- /// Acquires/locks this process for the specified thread if it's available.
- void Acquire(Thread* thread) override;
+ void UnpinThread(s32 core_id, KThread* thread) {
+ ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
+ ASSERT(thread != nullptr);
+ ASSERT(pinned_threads[core_id] == thread);
+ pinned_threads[core_id] = nullptr;
+ }
/// Changes the process status. If the status is different
/// from the current process status, then this will trigger
@@ -346,7 +402,7 @@ private:
u32 system_resource_size = 0;
/// Resource limit descriptor for this process
- std::shared_ptr<ResourceLimit> resource_limit;
+ std::shared_ptr<KResourceLimit> resource_limit;
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
@@ -373,12 +429,12 @@ private:
HandleTable handle_table;
/// Per-process address arbiter.
- AddressArbiter address_arbiter;
+ KAddressArbiter address_arbiter;
/// The per-process mutex lock instance used for handling various
/// forms of services, such as lock arbitration, and condition
/// variable related facilities.
- Mutex mutex;
+ KConditionVariable condition_var;
/// Address indicating the location of the process' dedicated TLS region.
VAddr tls_region_address = 0;
@@ -387,10 +443,7 @@ private:
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
/// List of threads that are running with this process as their owner.
- std::list<const Thread*> thread_list;
-
- /// List of threads waiting for a condition variable
- std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
+ std::list<const KThread*> thread_list;
/// Address of the top of the main thread's stack
VAddr main_thread_stack_top{};
@@ -410,6 +463,21 @@ private:
/// Schedule count of this process
s64 schedule_count{};
+ bool is_signaled{};
+ bool is_suspended{};
+
+ std::atomic<s32> num_created_threads{};
+ std::atomic<u16> num_threads{};
+ u16 peak_num_threads{};
+
+ std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
+ std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
+ std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
+
+ KThread* exception_thread{};
+
+ KLightLock state_lock;
+
/// System context
Core::System& system;
};
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 0f128c586..7c567049e 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <bit>
+
#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/svc_results.h"
namespace Kernel {
namespace {
@@ -60,7 +62,7 @@ constexpr CapabilityType GetCapabilityType(u32 value) {
u32 GetFlagBitOffset(CapabilityType type) {
const auto value = static_cast<u32>(type);
- return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
+ return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value)));
}
} // Anonymous namespace
@@ -121,13 +123,13 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
// 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;
+ return ResultInvalidCombination;
}
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;
+ return ResultInvalidCombination;
}
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table);
@@ -157,7 +159,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
- return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+ return ResultInvalidCapabilityDescriptor;
}
// Bail early on ignorable entries, as one would expect,
@@ -174,7 +176,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
LOG_ERROR(Kernel,
"Attempted to initialize flags that may only be initialized once. set_flags={}",
set_flags);
- return ERR_INVALID_COMBINATION;
+ return ResultInvalidCombination;
}
set_flags |= set_flag;
@@ -200,7 +202,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s
}
LOG_ERROR(Kernel, "Invalid capability type! type={}", type);
- return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+ return ResultInvalidCapabilityDescriptor;
}
void ProcessCapabilities::Clear() {
@@ -223,7 +225,7 @@ 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;
+ return ResultInvalidCapabilityDescriptor;
}
const u32 core_num_min = (flags >> 16) & 0xFF;
@@ -231,7 +233,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
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;
+ return ResultInvalidCombination;
}
const u32 priority_min = (flags >> 10) & 0x3F;
@@ -240,13 +242,13 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
LOG_ERROR(Kernel,
"Priority min is greater than priority max! priority_min={}, priority_max={}",
core_num_min, priority_max);
- return ERR_INVALID_COMBINATION;
+ return ResultInvalidCombination;
}
// 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;
+ return ResultInvalidCoreId;
}
const auto make_mask = [](u64 min, u64 max) {
@@ -267,7 +269,7 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
// If we've already set this svc before, bail.
if ((set_svc_bits & svc_bit) != 0) {
- return ERR_INVALID_COMBINATION;
+ return ResultInvalidCombination;
}
set_svc_bits |= svc_bit;
@@ -281,7 +283,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;
+ return ResultOutOfRange;
}
svc_capabilities[svc_number] = true;
@@ -319,7 +321,7 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
if (interrupt >= interrupt_capabilities.size()) {
LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}",
interrupt);
- return ERR_OUT_OF_RANGE;
+ return ResultOutOfRange;
}
interrupt_capabilities[interrupt] = true;
@@ -332,7 +334,7 @@ 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;
+ return ResultReservedValue;
}
program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
@@ -352,7 +354,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
LOG_ERROR(Kernel,
"Kernel version is non zero or flags are too small! major_version={}, flags={}",
major_version, flags);
- return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+ return ResultInvalidCapabilityDescriptor;
}
kernel_version = flags;
@@ -363,7 +365,7 @@ 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;
+ return ResultReservedValue;
}
handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
@@ -374,7 +376,7 @@ 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;
+ return ResultReservedValue;
}
is_debuggable = (flags & 0x20000) != 0;
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
deleted file mode 100644
index cea262ce0..000000000
--- a/src/core/hle/kernel/readable_event.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "core/hle/kernel/errors.h"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/thread.h"
-
-namespace Kernel {
-
-ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} {}
-ReadableEvent::~ReadableEvent() = default;
-
-bool ReadableEvent::ShouldWait(const Thread* thread) const {
- return !is_signaled;
-}
-
-void ReadableEvent::Acquire(Thread* thread) {
- ASSERT_MSG(IsSignaled(), "object unavailable!");
-}
-
-void ReadableEvent::Signal() {
- if (is_signaled) {
- return;
- }
-
- is_signaled = true;
- SynchronizationObject::Signal();
-}
-
-void ReadableEvent::Clear() {
- is_signaled = false;
-}
-
-ResultCode ReadableEvent::Reset() {
- KScopedSchedulerLock 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;
- }
-
- Clear();
-
- return RESULT_SUCCESS;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h
deleted file mode 100644
index 3264dd066..000000000
--- a/src/core/hle/kernel/readable_event.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/synchronization_object.h"
-
-union ResultCode;
-
-namespace Kernel {
-
-class KernelCore;
-class WritableEvent;
-
-class ReadableEvent final : public SynchronizationObject {
- friend class WritableEvent;
-
-public:
- ~ReadableEvent() override;
-
- std::string GetTypeName() const override {
- return "ReadableEvent";
- }
- std::string GetName() const override {
- return name;
- }
-
- static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
-
- bool ShouldWait(const Thread* thread) const override;
- void Acquire(Thread* thread) override;
-
- /// Unconditionally clears the readable event's state.
- void Clear();
-
- /// Clears the readable event's state if and only if it
- /// has already been signaled.
- ///
- /// @pre The event must be in a signaled state. If this event
- /// is in an unsignaled state and this function is called,
- /// then ERR_INVALID_STATE will be returned.
- ResultCode Reset();
-
- void Signal() override;
-
-private:
- explicit ReadableEvent(KernelCore& kernel);
-
- std::string name; ///< Name of event (optional)
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
deleted file mode 100644
index 7bf50339d..000000000
--- a/src/core/hle/kernel/resource_limit.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/hle/kernel/errors.h"
-#include "core/hle/kernel/resource_limit.h"
-#include "core/hle/result.h"
-
-namespace Kernel {
-namespace {
-constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
- return static_cast<std::size_t>(type);
-}
-} // Anonymous namespace
-
-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 limit.at(ResourceTypeToIndex(resource)) - current.at(ResourceTypeToIndex(resource));
-}
-
-s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
- return limit.at(ResourceTypeToIndex(resource));
-}
-
-ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
- 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={}", resource,
- value, index);
- return ERR_INVALID_STATE;
- }
-}
-} // namespace Kernel
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
deleted file mode 100644
index 936cc4d0f..000000000
--- a/src/core/hle/kernel/resource_limit.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#include <memory>
-
-#include "common/common_types.h"
-#include "core/hle/kernel/object.h"
-
-union ResultCode;
-
-namespace Kernel {
-
-class KernelCore;
-
-enum class ResourceType : u32 {
- PhysicalMemory,
- Threads,
- Events,
- TransferMemory,
- Sessions,
-
- // Used as a count, not an actual type.
- ResourceTypeCount
-};
-
-constexpr bool IsValidResourceType(ResourceType type) {
- return type < ResourceType::ResourceTypeCount;
-}
-
-class ResourceLimit final : public Object {
-public:
- explicit ResourceLimit(KernelCore& kernel);
- ~ResourceLimit() override;
-
- /// Creates a resource limit object.
- static std::shared_ptr<ResourceLimit> Create(KernelCore& kernel);
-
- std::string GetTypeName() const override {
- return "ResourceLimit";
- }
- std::string GetName() const override {
- return GetTypeName();
- }
-
- static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
- HandleType GetHandleType() const override {
- 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
- * @returns The current value of the resource type
- */
- s64 GetCurrentResourceValue(ResourceType resource) const;
-
- /**
- * Gets the max value for the specified resource.
- * @param resource Requested resource type
- * @returns The max value of the resource type
- */
- s64 GetMaxResourceValue(ResourceType resource) const;
-
- /**
- * Sets the limit value for a given resource type.
- *
- * @param resource The resource type to apply the limit to.
- * @param value The limit to apply to the given resource type.
- *
- * @return A result code indicating if setting the limit value
- * was successful or not.
- *
- * @note The supplied limit value *must* be greater than or equal to
- * the current resource value for the given resource type,
- * otherwise ERR_INVALID_STATE will be returned.
- */
- ResultCode SetLimitValue(ResourceType resource, s64 value);
-
-private:
- // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
- // functions
- //
- // Currently we have no way of distinguishing if a Create was called by the running application,
- // or by a service module. Approach this once we have separated the service modules into their
- // own processes
-
- using ResourceArray =
- std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
-
- ResourceArray limit{};
- ResourceArray current{};
- ResourceArray available{};
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index a549ae9d7..5d17346ad 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -5,20 +5,20 @@
#include <tuple>
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/svc_results.h"
namespace Kernel {
-ServerPort::ServerPort(KernelCore& kernel) : SynchronizationObject{kernel} {}
+ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
ServerPort::~ServerPort() = default;
ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
if (pending_sessions.empty()) {
- return ERR_NOT_FOUND;
+ return ResultNotFound;
}
auto session = std::move(pending_sessions.back());
@@ -28,15 +28,9 @@ ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) {
pending_sessions.push_back(std::move(pending_session));
-}
-
-bool ServerPort::ShouldWait(const Thread* thread) const {
- // If there are no pending sessions, we wait until a new one is added.
- return pending_sessions.empty();
-}
-
-void ServerPort::Acquire(Thread* thread) {
- ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
+ if (pending_sessions.size() == 1) {
+ NotifyAvailable();
+ }
}
bool ServerPort::IsSignaled() const {
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 41b191b86..29b4f2509 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -9,8 +9,8 @@
#include <utility>
#include <vector>
#include "common/common_types.h"
+#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/synchronization_object.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -20,7 +20,7 @@ class KernelCore;
class ServerSession;
class SessionRequestHandler;
-class ServerPort final : public SynchronizationObject {
+class ServerPort final : public KSynchronizationObject {
public:
explicit ServerPort(KernelCore& kernel);
~ServerPort() override;
@@ -79,11 +79,10 @@ public:
/// waiting to be accepted by this port.
void AppendPendingSession(std::shared_ptr<ServerSession> pending_session);
- bool ShouldWait(const Thread* thread) const override;
- void Acquire(Thread* thread) override;
-
bool IsSignaled() const override;
+ void Finalize() override {}
+
private:
/// ServerSessions waiting to be accepted by the port
std::vector<std::shared_ptr<ServerSession>> pending_sessions;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index b40fe3916..790dbb998 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -15,16 +15,16 @@
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
-#include "core/hle/kernel/thread.h"
#include "core/memory.h"
namespace Kernel {
-ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
+ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
ServerSession::~ServerSession() {
kernel.ReleaseServiceThread(service_thread);
@@ -42,16 +42,6 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
return MakeResult(std::move(session));
}
-bool ServerSession::ShouldWait(const Thread* thread) const {
- // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
- if (!parent->Client()) {
- return false;
- }
-
- // Wait if we have no pending requests, or if we're currently handling a request.
- return pending_requesting_threads.empty() || currently_handling != nullptr;
-}
-
bool ServerSession::IsSignaled() const {
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
if (!parent->Client()) {
@@ -62,15 +52,6 @@ bool ServerSession::IsSignaled() const {
return !pending_requesting_threads.empty() && currently_handling == nullptr;
}
-void ServerSession::Acquire(Thread* thread) {
- ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
- // We are now handling a request, pop it from the stack.
- // TODO(Subv): What happens if the client endpoint is closed before any requests are made?
- ASSERT(!pending_requesting_threads.empty());
- currently_handling = pending_requesting_threads.back();
- pending_requesting_threads.pop_back();
-}
-
void ServerSession::ClientDisconnected() {
// We keep a shared pointer to the hle handler to keep it alive throughout
// the call to ClientDisconnected, as ClientDisconnected invalidates the
@@ -135,7 +116,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
return RESULT_SUCCESS;
}
-ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
+ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory) {
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
auto context =
@@ -172,15 +153,15 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
{
KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) {
- context.GetThread().ResumeFromWait();
- context.GetThread().SetSynchronizationResults(nullptr, result);
+ context.GetThread().Wakeup();
+ context.GetThread().SetSyncedObject(nullptr, result);
}
}
return result;
}
-ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
+ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) {
return QueueSyncRequest(std::move(thread), memory);
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index e8d1d99ea..c42d5ee59 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -10,8 +10,8 @@
#include <vector>
#include "common/threadsafe_queue.h"
+#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/service_thread.h"
-#include "core/hle/kernel/synchronization_object.h"
#include "core/hle/result.h"
namespace Core::Memory {
@@ -29,7 +29,7 @@ class HLERequestContext;
class KernelCore;
class Session;
class SessionRequestHandler;
-class Thread;
+class KThread;
/**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -43,7 +43,7 @@ class Thread;
* After the server replies to the request, the response is marshalled back to the caller's
* TLS buffer and control is transferred back to it.
*/
-class ServerSession final : public SynchronizationObject {
+class ServerSession final : public KSynchronizationObject {
friend class ServiceThread;
public:
@@ -77,8 +77,6 @@ public:
return parent.get();
}
- bool IsSignaled() const override;
-
/**
* Sets the HLE handler for the session. This handler will be called to service IPC requests
* instead of the regular IPC machinery. (The regular IPC machinery is currently not
@@ -97,13 +95,9 @@ public:
*
* @returns ResultCode from the operation.
*/
- ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
+ ResultCode HandleSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing);
- bool ShouldWait(const Thread* thread) const override;
-
- void Acquire(Thread* thread) override;
-
/// Called when a client disconnection occurs.
void ClientDisconnected();
@@ -130,9 +124,13 @@ public:
convert_to_domain = true;
}
+ bool IsSignaled() const override;
+
+ void Finalize() override {}
+
private:
/// Queues a sync request from the emulated application.
- ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
+ ResultCode QueueSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory);
/// Completes a sync request from the emulated application.
ResultCode CompleteSyncRequest(HLERequestContext& context);
@@ -153,12 +151,12 @@ private:
/// List of threads that are pending a response after a sync request. This list is processed in
/// a LIFO manner, thus, the last request will be dispatched first.
/// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
- std::vector<std::shared_ptr<Thread>> pending_requesting_threads;
+ std::vector<std::shared_ptr<KThread>> pending_requesting_threads;
/// Thread whose request is currently being handled. A request is considered "handled" when a
/// response is sent via svcReplyAndReceive.
/// TODO(Subv): Find a better name for this.
- std::shared_ptr<Thread> currently_handling;
+ std::shared_ptr<KThread> currently_handling;
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};
diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp
index e4dd53e24..8830d4e91 100644
--- a/src/core/hle/kernel/session.cpp
+++ b/src/core/hle/kernel/session.cpp
@@ -4,15 +4,23 @@
#include "common/assert.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
namespace Kernel {
-Session::Session(KernelCore& kernel) : SynchronizationObject{kernel} {}
-Session::~Session() = default;
+Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {}
+Session::~Session() {
+ // Release reserved resource when the Session pair was created.
+ kernel.GetSystemResourceLimit()->Release(LimitableResource::Sessions, 1);
+}
Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
+ // Reserve a new session from the resource limit.
+ KScopedResourceReservation session_reservation(kernel.GetSystemResourceLimit(),
+ LimitableResource::Sessions);
+ ASSERT(session_reservation.Succeeded());
auto session{std::make_shared<Session>(kernel)};
auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()};
auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()};
@@ -21,21 +29,13 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
session->client = client_session;
session->server = server_session;
+ session_reservation.Commit();
return std::make_pair(std::move(client_session), std::move(server_session));
}
-bool Session::ShouldWait(const Thread* thread) const {
- UNIMPLEMENTED();
- return {};
-}
-
bool Session::IsSignaled() const {
UNIMPLEMENTED();
return true;
}
-void Session::Acquire(Thread* thread) {
- UNIMPLEMENTED();
-}
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
index 7cd9c0d77..fa3c5651a 100644
--- a/src/core/hle/kernel/session.h
+++ b/src/core/hle/kernel/session.h
@@ -8,7 +8,7 @@
#include <string>
#include <utility>
-#include "core/hle/kernel/synchronization_object.h"
+#include "core/hle/kernel/k_synchronization_object.h"
namespace Kernel {
@@ -19,7 +19,7 @@ class ServerSession;
* Parent structure to link the client and server endpoints of a session with their associated
* client port.
*/
-class Session final : public SynchronizationObject {
+class Session final : public KSynchronizationObject {
public:
explicit Session(KernelCore& kernel);
~Session() override;
@@ -37,11 +37,9 @@ public:
return HANDLE_TYPE;
}
- bool ShouldWait(const Thread* thread) const override;
-
bool IsSignaled() const override;
- void Acquire(Thread* thread) override;
+ void Finalize() override {}
std::shared_ptr<ClientSession> Client() {
if (auto result{client.lock()}) {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 0cd467110..2eadd51d7 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -4,6 +4,7 @@
#include "common/assert.h"
#include "core/core.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/shared_memory.h"
@@ -13,7 +14,9 @@ namespace Kernel {
SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
: Object{kernel}, device_memory{device_memory} {}
-SharedMemory::~SharedMemory() = default;
+SharedMemory::~SharedMemory() {
+ kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
+}
std::shared_ptr<SharedMemory> SharedMemory::Create(
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
@@ -21,6 +24,11 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
std::string name) {
+ const auto resource_limit = kernel.GetSystemResourceLimit();
+ KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
+ size);
+ ASSERT(memory_reservation.Succeeded());
+
std::shared_ptr<SharedMemory> shared_memory{
std::make_shared<SharedMemory>(kernel, device_memory)};
@@ -32,6 +40,7 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
shared_memory->size = size;
shared_memory->name = name;
+ memory_reservation.Commit();
return shared_memory;
}
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 0ef87235c..623bd8b11 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -71,6 +71,8 @@ public:
return device_memory.GetPointer(physical_address + offset);
}
+ void Finalize() override {}
+
private:
Core::DeviceMemory& device_memory;
Process* owner_process{};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index de3ed25da..31d899e06 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -10,39 +10,44 @@
#include "common/alignment.h"
#include "common/assert.h"
+#include "common/common_funcs.h"
#include "common/fiber.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
+#include "common/scope_exit.h"
#include "common/string_util.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/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/k_address_arbiter.h"
+#include "core/hle/kernel/k_condition_variable.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block.h"
+#include "core/hle/kernel/memory/memory_layout.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/shared_memory.h"
#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.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"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
@@ -66,49 +71,49 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
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;
+ return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(src_addr)) {
LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is 0");
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (!IsValidAddressRange(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
dst_addr, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (!IsValidAddressRange(src_addr, size)) {
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
src_addr, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
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;
+ return ResultInvalidCurrentMemory;
}
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;
+ return ResultInvalidMemoryRange;
}
if (manager.IsInsideHeapRegion(dst_addr, size)) {
@@ -116,7 +121,7 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
"Destination does not fit within the heap region, addr=0x{:016X}, "
"size=0x{:016X}",
dst_addr, size);
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
if (manager.IsInsideAliasRegion(dst_addr, size)) {
@@ -124,7 +129,7 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
"Destination does not fit within the map region, addr=0x{:016X}, "
"size=0x{:016X}",
dst_addr, size);
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
return RESULT_SUCCESS;
@@ -133,33 +138,40 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds
enum class ResourceLimitValueType {
CurrentValue,
LimitValue,
+ PeakValue,
};
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);
+ const auto type = static_cast<LimitableResource>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
- return ERR_INVALID_ENUM_VALUE;
+ return ResultInvalidEnumValue;
}
const auto* const current_process = system.Kernel().CurrentProcess();
ASSERT(current_process != nullptr);
const auto resource_limit_object =
- current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
+ current_process->GetHandleTable().Get<KResourceLimit>(resource_limit);
if (!resource_limit_object) {
LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
resource_limit);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
- if (value_type == ResourceLimitValueType::CurrentValue) {
- return MakeResult(resource_limit_object->GetCurrentResourceValue(type));
+ switch (value_type) {
+ case ResourceLimitValueType::CurrentValue:
+ return MakeResult(resource_limit_object->GetCurrentValue(type));
+ case ResourceLimitValueType::LimitValue:
+ return MakeResult(resource_limit_object->GetLimitValue(type));
+ case ResourceLimitValueType::PeakValue:
+ return MakeResult(resource_limit_object->GetPeakValue(type));
+ default:
+ LOG_ERROR(Kernel_SVC, "Invalid resource value_type: '{}'", value_type);
+ return ResultInvalidEnumValue;
}
-
- return MakeResult(resource_limit_object->GetMaxResourceValue(type));
}
} // Anonymous namespace
@@ -172,12 +184,12 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s
if ((heap_size % 0x200000) != 0) {
LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
heap_size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (heap_size >= 0x200000000) {
LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
@@ -203,19 +215,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
if (!Common::Is4KBAligned(address)) {
LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
size);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (!IsValidAddressRange(address, size)) {
LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
address, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)};
@@ -224,7 +236,7 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
LOG_ERROR(Kernel_SVC,
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
attribute, mask);
- return ERR_INVALID_COMBINATION;
+ return ResultInvalidCombination;
}
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
@@ -288,7 +300,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
LOG_ERROR(Kernel_SVC,
"Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
port_name_address);
- return ERR_NOT_FOUND;
+ return ResultNotFound;
}
static constexpr std::size_t PortNameMaxLength = 11;
@@ -297,7 +309,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
if (port_name.size() > PortNameMaxLength) {
LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
port_name.size());
- return ERR_OUT_OF_RANGE;
+ return ResultOutOfRange;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -306,11 +318,9 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
const auto it = kernel.FindNamedPort(port_name);
if (!kernel.IsValidNamedPort(it)) {
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
- return ERR_NOT_FOUND;
+ return ResultNotFound;
}
- ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1));
-
auto client_port = it->second;
std::shared_ptr<ClientSession> client_session;
@@ -335,7 +345,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
std::shared_ptr<ClientSession> session = handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
@@ -343,28 +353,13 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
- thread->InvalidateHLECallback();
- thread->SetStatus(ThreadStatus::WaitIPC);
+ thread->SetState(ThreadState::Waiting);
+ thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
}
- if (thread->HasHLECallback()) {
- Handle event_handle = thread->GetHLETimeEvent();
- if (event_handle != InvalidHandle) {
- auto& time_manager = kernel.TimeManager();
- time_manager.UnscheduleTimeEvent(event_handle);
- }
-
- {
- KScopedSchedulerLock lock(kernel);
- auto* sync_object = thread->GetHLESyncObject();
- sync_object->RemoveWaitingThread(SharedFrom(thread));
- }
-
- thread->InvokeHLECallback(SharedFrom(thread));
- }
-
- return thread->GetSignalingResult();
+ KSynchronizationObject* dummy{};
+ return thread->GetWaitResult(std::addressof(dummy));
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -372,27 +367,29 @@ static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
}
/// Get the ID for the specified thread.
-static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
+static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
+ // Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
+ return ResultInvalidHandle;
}
- *thread_id = thread->GetThreadID();
+ // Get the thread's id.
+ *out_thread_id = thread->GetThreadID();
return RESULT_SUCCESS;
}
-static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high,
- Handle thread_handle) {
- u64 thread_id{};
- const ResultCode result{GetThreadId(system, &thread_id, thread_handle)};
+static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
+ u32* out_thread_id_high, Handle thread_handle) {
+ u64 out_thread_id{};
+ const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)};
- *thread_id_low = static_cast<u32>(thread_id >> 32);
- *thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max());
+ *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
+ *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
return result;
}
@@ -408,12 +405,12 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
return RESULT_SUCCESS;
}
- const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
if (thread) {
const Process* const owner_process = thread->GetOwnerProcess();
if (!owner_process) {
LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
*process_id = owner_process->GetProcessID();
@@ -423,7 +420,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
// NOTE: This should also handle debug objects before returning.
LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high,
@@ -436,7 +433,7 @@ static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32*
}
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
+static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
u64 handle_count, s64 nano_seconds) {
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
handles_address, handle_count, nano_seconds);
@@ -446,7 +443,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
LOG_ERROR(Kernel_SVC,
"Handle address is not a valid virtual address, handle_address=0x{:016X}",
handles_address);
- return ERR_INVALID_POINTER;
+ return ResultInvalidPointer;
}
static constexpr u64 MaxHandles = 0x40;
@@ -454,32 +451,30 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
if (handle_count > MaxHandles) {
LOG_ERROR(Kernel_SVC, "Handle count specified is too large, expected {} but got {}",
MaxHandles, handle_count);
- return ERR_OUT_OF_RANGE;
+ return ResultOutOfRange;
}
auto& kernel = system.Kernel();
- Thread::ThreadSynchronizationObjects objects(handle_count);
+ std::vector<KSynchronizationObject*> objects(handle_count);
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
for (u64 i = 0; i < handle_count; ++i) {
const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
- const auto object = handle_table.Get<SynchronizationObject>(handle);
+ const auto object = handle_table.Get<KSynchronizationObject>(handle);
if (object == nullptr) {
LOG_ERROR(Kernel_SVC, "Object is a nullptr");
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
- objects[i] = object;
+ objects[i] = object.get();
}
- auto& synchronization = kernel.Synchronization();
- const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds);
- *index = handle_result;
- return result;
+ return KSynchronizationObject::Wait(kernel, index, objects.data(),
+ static_cast<s32>(objects.size()), nano_seconds);
}
static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
- s32 handle_count, u32 timeout_high, Handle* index) {
+ s32 handle_count, u32 timeout_high, s32* index) {
const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);
}
@@ -488,15 +483,17 @@ static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u
static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
+ // Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
+
if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
- thread_handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
+ return ResultInvalidHandle;
}
- thread->CancelWait();
+ // Cancel the thread's wait.
+ thread->WaitCancel();
return RESULT_SUCCESS;
}
@@ -504,56 +501,53 @@ static ResultCode CancelSynchronization32(Core::System& system, Handle thread_ha
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) {
- LOG_TRACE(Kernel_SVC,
- "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, "
- "requesting_current_thread_handle=0x{:08X}",
- holding_thread_handle, mutex_addr, requesting_thread_handle);
+/// Attempts to locks a mutex
+static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address,
+ u32 tag) {
+ LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
+ thread_handle, address, tag);
- 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;
+ // Validate the input address.
+ if (Memory::IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
+ address);
+ return ResultInvalidCurrentMemory;
}
-
- if (!Common::IsWordAligned(mutex_addr)) {
- LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
- return ERR_INVALID_ADDRESS;
+ if (!Common::IsAligned(address, sizeof(u32))) {
+ LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
+ return ResultInvalidAddress;
}
- auto* const current_process = system.Kernel().CurrentProcess();
- return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
- requesting_thread_handle);
+ return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
}
-static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle,
- u32 mutex_addr, Handle requesting_thread_handle) {
- return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle);
+static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address,
+ u32 tag) {
+ return ArbitrateLock(system, thread_handle, address, tag);
}
/// Unlock a mutex
-static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
- LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
+static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
+ LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
- 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;
- }
+ // Validate the input address.
- if (!Common::IsWordAligned(mutex_addr)) {
- LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
- return ERR_INVALID_ADDRESS;
+ if (Memory::IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC,
+ "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
+ address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(u32))) {
+ LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
+ return ResultInvalidAddress;
}
- auto* const current_process = system.Kernel().CurrentProcess();
- return current_process->GetMutex().Release(mutex_addr);
+ return system.Kernel().CurrentProcess()->SignalToAddress(address);
}
-static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) {
- return ArbitrateUnlock(system, mutex_addr);
+static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) {
+ return ArbitrateUnlock(system, address);
}
enum class BreakType : u32 {
@@ -664,7 +658,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2);
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
- const auto thread_processor_id = current_thread->GetProcessorID();
+ const auto thread_processor_id = current_thread->GetActiveCore();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
}
}
@@ -748,7 +742,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
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;
+ return ResultInvalidEnumValue;
}
const auto& current_process_handle_table =
@@ -757,7 +751,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
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;
+ return ResultInvalidHandle;
}
switch (info_id_type) {
@@ -839,7 +833,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
}
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
- return ERR_INVALID_ENUM_VALUE;
+ return ResultInvalidEnumValue;
}
case GetInfoType::IsCurrentProcessBeingDebugged:
@@ -849,13 +843,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;
+ return ResultInvalidHandle;
}
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;
+ return ResultInvalidCombination;
}
Process* const current_process = system.Kernel().CurrentProcess();
@@ -880,13 +874,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (handle != 0) {
LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
Process::RANDOM_ENTROPY_SIZE, info_sub_id);
- return ERR_INVALID_COMBINATION;
+ return ResultInvalidCombination;
}
*result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
@@ -903,15 +897,15 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
info_sub_id);
- return ERR_INVALID_COMBINATION;
+ return ResultInvalidCombination;
}
- const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>(
+ const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<KThread>(
static_cast<Handle>(handle));
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
static_cast<Handle>(handle));
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
const auto& core_timing = system.CoreTiming();
@@ -922,7 +916,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
- const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
+ const u64 thread_ticks = current_thread->GetCpuTime();
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
@@ -935,7 +929,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
default:
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
- return ERR_INVALID_ENUM_VALUE;
+ return ResultInvalidEnumValue;
}
}
@@ -958,22 +952,22 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is zero");
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (!(addr < addr + size)) {
LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
Process* const current_process{system.Kernel().CurrentProcess()};
@@ -981,21 +975,21 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
if (current_process->GetSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
- return ERR_INVALID_STATE;
+ return ResultInvalidState;
}
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;
+ return ResultInvalidMemoryRange;
}
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 ResultInvalidMemoryRange;
}
return page_table.MapPhysicalMemory(addr, size);
@@ -1012,22 +1006,22 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is zero");
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (!(addr < addr + size)) {
LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
Process* const current_process{system.Kernel().CurrentProcess()};
@@ -1035,21 +1029,21 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
if (current_process->GetSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
- return ERR_INVALID_STATE;
+ return ResultInvalidState;
}
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;
+ return ResultInvalidMemoryRange;
}
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 ResultInvalidMemoryRange;
}
return page_table.UnmapPhysicalMemory(addr, size);
@@ -1060,128 +1054,139 @@ static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size
}
/// Sets the thread activity
-static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
- LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
- if (activity > static_cast<u32>(ThreadActivity::Paused)) {
- return ERR_INVALID_ENUM_VALUE;
+static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
+ ThreadActivity thread_activity) {
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
+ thread_activity);
+
+ // Validate the activity.
+ constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
+ return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
+ };
+ if (!IsValidThreadActivity(thread_activity)) {
+ LOG_ERROR(Kernel_SVC, "Invalid thread activity value provided (activity={})",
+ thread_activity);
+ return ResultInvalidEnumValue;
}
- const auto* current_process = system.Kernel().CurrentProcess();
- const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+ // Get the thread from its handle.
+ auto& kernel = system.Kernel();
+ const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
+ return ResultInvalidHandle;
}
- if (thread->GetOwnerProcess() != current_process) {
- LOG_ERROR(Kernel_SVC,
- "The current process does not own the current thread, thread_handle={:08X} "
- "thread_pid={}, "
- "current_process_pid={}",
- handle, thread->GetOwnerProcess()->GetProcessID(),
- current_process->GetProcessID());
- return ERR_INVALID_HANDLE;
+ // Check that the activity is being set on a non-current thread for the current process.
+ if (thread->GetOwnerProcess() != kernel.CurrentProcess()) {
+ LOG_ERROR(Kernel_SVC, "Invalid owning process for the created thread.");
+ return ResultInvalidHandle;
+ }
+ if (thread.get() == GetCurrentThreadPointer(kernel)) {
+ LOG_ERROR(Kernel_SVC, "Thread is busy");
+ return ResultBusy;
}
- if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
- LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
- return ERR_BUSY;
+ // Set the activity.
+ const auto set_result = thread->SetActivity(thread_activity);
+ if (set_result.IsError()) {
+ LOG_ERROR(Kernel_SVC, "Failed to set thread activity.");
+ return set_result;
}
- return thread->SetActivity(static_cast<ThreadActivity>(activity));
+ return RESULT_SUCCESS;
}
-static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) {
- return SetThreadActivity(system, handle, activity);
+static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle,
+ Svc::ThreadActivity thread_activity) {
+ return SetThreadActivity(system, thread_handle, thread_activity);
}
/// Gets the thread context
-static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) {
- LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
+static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
+ LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
+ thread_handle);
+ // Get the thread from its handle.
const auto* current_process = system.Kernel().CurrentProcess();
- const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+ const std::shared_ptr<KThread> thread =
+ current_process->GetHandleTable().Get<KThread>(thread_handle);
if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={})", thread_handle);
+ return ResultInvalidHandle;
}
+ // Require the handle be to a non-current thread in the current process.
if (thread->GetOwnerProcess() != current_process) {
- LOG_ERROR(Kernel_SVC,
- "The current process does not own the current thread, thread_handle={:08X} "
- "thread_pid={}, "
- "current_process_pid={}",
- handle, thread->GetOwnerProcess()->GetProcessID(),
- current_process->GetProcessID());
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Thread owning process is not the current process.");
+ return ResultInvalidHandle;
}
-
if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
- LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
- return ERR_BUSY;
+ LOG_ERROR(Kernel_SVC, "Current thread is busy.");
+ return ResultBusy;
}
- Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64();
- // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
- ctx.pstate &= 0xFF0FFE20;
-
- // If 64-bit, we can just write the context registers directly and we're good.
- // However, if 32-bit, we have to ensure some registers are zeroed out.
- if (!current_process->Is64BitProcess()) {
- std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0);
- std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
+ // Get the thread context.
+ std::vector<u8> context;
+ const auto context_result = thread->GetThreadContext3(context);
+ if (context_result.IsError()) {
+ LOG_ERROR(Kernel_SVC, "Unable to successfully retrieve thread context (result: {})",
+ context_result.raw);
+ return context_result;
}
- system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx));
+ // Copy the thread context to user space.
+ system.Memory().WriteBlock(out_context, context.data(), context.size());
+
return RESULT_SUCCESS;
}
-static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) {
- return GetThreadContext(system, thread_context, handle);
+static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
+ return GetThreadContext(system, out_context, thread_handle);
}
/// Gets the priority for the specified thread
-static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
+static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
LOG_TRACE(Kernel_SVC, "called");
+ // Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
if (!thread) {
- *priority = 0;
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", handle);
+ return ResultInvalidHandle;
}
- *priority = thread->GetPriority();
+ // Get the thread's priority.
+ *out_priority = thread->GetPriority();
return RESULT_SUCCESS;
}
-static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) {
- return GetThreadPriority(system, priority, handle);
+static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
+ return GetThreadPriority(system, out_priority, handle);
}
/// Sets the priority for the specified thread
static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
LOG_TRACE(Kernel_SVC, "called");
- if (priority > THREADPRIO_LOWEST) {
- LOG_ERROR(
- Kernel_SVC,
- "An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
- THREADPRIO_LOWEST, priority, handle);
- return ERR_INVALID_THREAD_PRIORITY;
+ // Validate the priority.
+ if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
+ LOG_ERROR(Kernel_SVC, "Invalid thread priority specified (priority={})", priority);
+ return ResultInvalidPriority;
}
- const auto* const current_process = system.Kernel().CurrentProcess();
-
- std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+ // Get the thread from its handle.
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid handle provided (handle={:08X})", handle);
+ return ResultInvalidHandle;
}
- thread->SetPriority(priority);
-
+ // Set the thread priority.
+ thread->SetBasePriority(priority);
return RESULT_SUCCESS;
}
@@ -1208,23 +1213,23 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (size == 0) {
LOG_ERROR(Kernel_SVC, "Size is 0");
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (!Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
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;
+ return ResultInvalidCurrentMemory;
}
const auto permission_type = static_cast<Memory::MemoryPermission>(permissions);
@@ -1232,7 +1237,7 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
Memory::MemoryPermission::ReadAndWrite) {
LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
permissions);
- return ERR_INVALID_MEMORY_PERMISSIONS;
+ return ResultInvalidMemoryPermissions;
}
auto* const current_process{system.Kernel().CurrentProcess()};
@@ -1243,7 +1248,7 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
"Addr does not fit within the valid region, addr=0x{:016X}, "
"size=0x{:016X}",
addr, size);
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
if (page_table.IsInsideHeapRegion(addr, size)) {
@@ -1251,7 +1256,7 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
"Addr does not fit within the heap region, addr=0x{:016X}, "
"size=0x{:016X}",
addr, size);
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
if (page_table.IsInsideAliasRegion(addr, size)) {
@@ -1259,14 +1264,14 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
"Address does not fit within the map region, addr=0x{:016X}, "
"size=0x{:016X}",
addr, size);
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
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;
+ return ResultInvalidHandle;
}
return shared_memory->Map(*current_process, addr, size, permission_type);
@@ -1287,7 +1292,7 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
if (!process) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
process_handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
auto& memory{system.Memory()};
@@ -1334,18 +1339,18 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
if (!Common::Is4KBAligned(src_address)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
src_address);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(dst_address)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
dst_address);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (!IsValidAddressRange(dst_address, size)) {
@@ -1353,7 +1358,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (!IsValidAddressRange(src_address, size)) {
@@ -1361,7 +1366,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
@@ -1369,7 +1374,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
if (!process) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
process_handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
auto& page_table = process->PageTable();
@@ -1378,7 +1383,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (!page_table.IsInsideASLRRegion(dst_address, size)) {
@@ -1386,7 +1391,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
"Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
return page_table.MapProcessCodeMemory(dst_address, src_address, size);
@@ -1402,18 +1407,18 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
if (!Common::Is4KBAligned(dst_address)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
dst_address);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(src_address)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
src_address);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (size == 0 || Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
- return ERR_INVALID_SIZE;
+ return ResultInvalidSize;
}
if (!IsValidAddressRange(dst_address, size)) {
@@ -1421,7 +1426,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (!IsValidAddressRange(src_address, size)) {
@@ -1429,7 +1434,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
@@ -1437,7 +1442,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
if (!process) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
process_handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
auto& page_table = process->PageTable();
@@ -1446,7 +1451,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
if (!page_table.IsInsideASLRRegion(dst_address, size)) {
@@ -1454,7 +1459,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
"Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
- return ERR_INVALID_MEMORY_RANGE;
+ return ResultInvalidMemoryRange;
}
return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
@@ -1472,62 +1477,67 @@ static void ExitProcess(Core::System& system) {
current_process->PrepareForTermination();
// Kill the current thread
- system.Kernel().CurrentScheduler()->GetCurrentThread()->Stop();
+ system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit();
}
static void ExitProcess32(Core::System& system) {
ExitProcess(system);
}
+static constexpr bool IsValidCoreId(int32_t core_id) {
+ return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
+}
+
/// Creates a new thread
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
- VAddr stack_top, u32 priority, s32 processor_id) {
+ VAddr stack_bottom, u32 priority, s32 core_id) {
LOG_DEBUG(Kernel_SVC,
- "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
- "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
- entry_point, arg, stack_top, priority, processor_id, *out_handle);
-
- auto* const current_process = system.Kernel().CurrentProcess();
+ "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
+ "priority=0x{:08X}, core_id=0x{:08X}",
+ entry_point, arg, stack_bottom, priority, core_id);
- if (processor_id == THREADPROCESSORID_IDEAL) {
- // Set the target CPU to the one specified by the process.
- processor_id = current_process->GetIdealCore();
- ASSERT(processor_id != THREADPROCESSORID_IDEAL);
+ // Adjust core id, if it's the default magic.
+ auto& kernel = system.Kernel();
+ auto& process = *kernel.CurrentProcess();
+ if (core_id == IdealCoreUseProcessValue) {
+ core_id = process.GetIdealCoreId();
}
- if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
- LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
- return ERR_INVALID_PROCESSOR_ID;
+ // Validate arguments.
+ if (!IsValidCoreId(core_id)) {
+ LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
+ return ResultInvalidCoreId;
}
-
- const u64 core_mask = current_process->GetCoreMask();
- if ((core_mask | (1ULL << processor_id)) != core_mask) {
- LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
- return ERR_INVALID_PROCESSOR_ID;
+ if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
+ LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
+ return ResultInvalidCoreId;
}
- if (priority > THREADPRIO_LOWEST) {
- LOG_ERROR(Kernel_SVC,
- "Invalid thread priority specified ({}). Must be within the range 0-64",
- priority);
- return ERR_INVALID_THREAD_PRIORITY;
+ if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
+ LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
+ return ResultInvalidPriority;
}
-
- if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
- LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
- return ERR_INVALID_THREAD_PRIORITY;
+ if (!process.CheckThreadPriority(priority)) {
+ LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
+ return ResultInvalidPriority;
}
- auto& kernel = system.Kernel();
-
- ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1));
+ KScopedResourceReservation thread_reservation(
+ kernel.CurrentProcess(), LimitableResource::Threads, 1,
+ system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
+ if (!thread_reservation.Succeeded()) {
+ LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
+ return ResultResourceLimitedExceeded;
+ }
- ThreadType type = THREADTYPE_USER;
- CASCADE_RESULT(std::shared_ptr<Thread> thread,
- Thread::Create(system, type, "", entry_point, priority, arg, processor_id,
- stack_top, current_process));
+ std::shared_ptr<KThread> thread;
+ {
+ KScopedLightLock lk{process.GetStateLock()};
+ CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority,
+ arg, core_id, stack_bottom, &process));
+ }
- const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
+ const auto new_thread_handle = process.GetHandleTable().Create(thread);
if (new_thread_handle.Failed()) {
LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
new_thread_handle.Code().raw);
@@ -1538,6 +1548,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
// Set the thread name for debugging purposes.
thread->SetName(
fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
+ thread_reservation.Commit();
return RESULT_SUCCESS;
}
@@ -1551,17 +1562,24 @@ static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 p
static ResultCode StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
+ // Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
- thread_handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle provided (handle={:08X})", thread_handle);
+ return ResultInvalidHandle;
}
- ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
+ // Try to start the thread.
+ const auto run_result = thread->Run();
+ if (run_result.IsError()) {
+ LOG_ERROR(Kernel_SVC,
+ "Unable to successfuly start thread (thread handle={:08X}, result={})",
+ thread_handle, run_result.raw);
+ return run_result;
+ }
- return thread->Start();
+ return RESULT_SUCCESS;
}
static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
@@ -1574,7 +1592,7 @@ static void ExitThread(Core::System& system) {
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread));
- current_thread->Stop();
+ current_thread->Exit();
}
static void ExitThread32(Core::System& system) {
@@ -1583,34 +1601,28 @@ static void ExitThread32(Core::System& system) {
/// Sleep the current thread
static void SleepThread(Core::System& system, s64 nanoseconds) {
- LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
+ auto& kernel = system.Kernel();
+ const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
- enum class SleepType : s64 {
- YieldWithoutCoreMigration = 0,
- YieldWithCoreMigration = -1,
- YieldAndWaitForLoadBalancing = -2,
- };
+ LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
- auto& scheduler = *system.Kernel().CurrentScheduler();
- if (nanoseconds <= 0) {
- switch (static_cast<SleepType>(nanoseconds)) {
- case SleepType::YieldWithoutCoreMigration: {
- scheduler.YieldWithoutCoreMigration();
- break;
- }
- case SleepType::YieldWithCoreMigration: {
- scheduler.YieldWithCoreMigration();
- break;
- }
- case SleepType::YieldAndWaitForLoadBalancing: {
- scheduler.YieldToAnyThread();
- break;
- }
- default:
- UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
- }
+ // When the input tick is positive, sleep.
+ if (nanoseconds > 0) {
+ // Convert the timeout from nanoseconds to ticks.
+ // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
+
+ // Sleep.
+ // NOTE: Nintendo does not check the result of this sleep.
+ static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
+ } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
+ KScheduler::YieldWithoutCoreMigration(kernel);
+ } else if (yield_type == Svc::YieldType::WithCoreMigration) {
+ KScheduler::YieldWithCoreMigration(kernel);
+ } else if (yield_type == Svc::YieldType::ToAnyThread) {
+ KScheduler::YieldToAnyThread(kernel);
} else {
- scheduler.GetCurrentThread()->Sleep(nanoseconds);
+ // Nintendo does nothing at all if an otherwise invalid value is passed.
+ UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
}
@@ -1620,224 +1632,159 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec
}
/// Wait process wide key atomic
-static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr,
- VAddr condition_variable_addr, Handle thread_handle,
- s64 nano_seconds) {
- LOG_TRACE(
- Kernel_SVC,
- "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
- mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
-
- if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
- LOG_ERROR(
- Kernel_SVC,
- "Given mutex address must not be within the kernel address space. address=0x{:016X}",
- mutex_addr);
- return ERR_INVALID_ADDRESS_STATE;
- }
-
- if (!Common::IsWordAligned(mutex_addr)) {
- LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}",
- mutex_addr);
- return ERR_INVALID_ADDRESS;
- }
-
- ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
- auto& kernel = system.Kernel();
- Handle event_handle;
- Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
- auto* const current_process = kernel.CurrentProcess();
- {
- KScopedSchedulerLockAndSleep 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;
- }
-
- 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));
- }
-
- if (event_handle != InvalidHandle) {
- auto& time_manager = kernel.TimeManager();
- time_manager.UnscheduleTimeEvent(event_handle);
- }
-
- {
- KScopedSchedulerLock lock(kernel);
-
- auto* owner = current_thread->GetLockOwner();
- if (owner != nullptr) {
- owner->RemoveMutexWaiter(SharedFrom(current_thread));
+static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key,
+ u32 tag, s64 timeout_ns) {
+ LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
+ cv_key, tag, timeout_ns);
+
+ // Validate input.
+ if (Memory::IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(s32))) {
+ LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
+ return ResultInvalidAddress;
+ }
+
+ // Convert timeout from nanoseconds to ticks.
+ s64 timeout{};
+ if (timeout_ns > 0) {
+ const s64 offset_tick(timeout_ns);
+ if (offset_tick > 0) {
+ timeout = offset_tick + 2;
+ if (timeout <= 0) {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = std::numeric_limits<s64>::max();
}
-
- current_process->RemoveConditionVariableThread(SharedFrom(current_thread));
+ } else {
+ timeout = timeout_ns;
}
- // Note: Deliberately don't attempt to inherit the lock owner's priority.
- return current_thread->GetSignalingResult();
+ // Wait on the condition variable.
+ return system.Kernel().CurrentProcess()->WaitConditionVariable(
+ address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
}
-static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr,
- u32 condition_variable_addr, Handle thread_handle,
- u32 nanoseconds_low, u32 nanoseconds_high) {
- const auto nanoseconds = static_cast<s64>(nanoseconds_low | (u64{nanoseconds_high} << 32));
- return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle,
- nanoseconds);
+static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
+ u32 timeout_ns_low, u32 timeout_ns_high) {
+ const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
+ return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
}
/// Signal process wide key
-static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) {
- LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
- condition_variable_addr, target);
+static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
+ LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
- ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
+ // Signal the condition variable.
+ return system.Kernel().CurrentProcess()->SignalConditionVariable(
+ Common::AlignDown(cv_key, sizeof(u32)), count);
+}
- // Retrieve a list of all threads that are waiting for this condition variable.
- auto& kernel = system.Kernel();
- KScopedSchedulerLock 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) {
- 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];
-
- ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
-
- // liberate Cond Var Thread.
- current_process->RemoveConditionVariableThread(thread);
-
- const std::size_t current_core = system.CurrentCoreIndex();
- auto& monitor = system.Monitor();
-
- // Atomically read the value of the mutex.
- u32 mutex_val = 0;
- u32 update_val = 0;
- const VAddr mutex_address = thread->GetMutexWaitAddress();
- do {
- // If the mutex is not yet acquired, acquire it.
- mutex_val = monitor.ExclusiveRead32(current_core, mutex_address);
-
- if (mutex_val != 0) {
- update_val = mutex_val | Mutex::MutexHasWaitersFlag;
- } else {
- 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.
- auto* const lock_owner = thread->GetLockOwner();
- if (lock_owner != nullptr) {
- lock_owner->RemoveMutexWaiter(thread);
- }
+static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
+ SignalProcessWideKey(system, cv_key, count);
+}
- thread->SetLockOwner(nullptr);
- 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);
- if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
- thread->SetStatus(ThreadStatus::WaitMutex);
- }
+namespace {
- owner->AddMutexWaiter(thread);
- }
+constexpr bool IsValidSignalType(Svc::SignalType type) {
+ switch (type) {
+ case Svc::SignalType::Signal:
+ case Svc::SignalType::SignalAndIncrementIfEqual:
+ case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
+ return true;
+ default:
+ return false;
}
}
-static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) {
- SignalProcessWideKey(system, condition_variable_addr, target);
+constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
+ switch (type) {
+ case Svc::ArbitrationType::WaitIfLessThan:
+ case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
+ case Svc::ArbitrationType::WaitIfEqual:
+ return true;
+ default:
+ return false;
+ }
}
-// Wait for an address (via Address Arbiter)
-static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
- s64 timeout) {
- LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address,
- type, value, timeout);
-
- // If the passed address is a kernel virtual address, return invalid memory state.
- if (Core::Memory::IsKernelVirtualAddress(address)) {
- LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
- return ERR_INVALID_ADDRESS_STATE;
- }
+} // namespace
- // If the address is not properly aligned to 4 bytes, return invalid address.
- if (!Common::IsWordAligned(address)) {
- LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
- return ERR_INVALID_ADDRESS;
+// Wait for an address (via Address Arbiter)
+static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
+ s32 value, s64 timeout_ns) {
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
+ address, arb_type, value, timeout_ns);
+
+ // Validate input.
+ if (Memory::IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(s32))) {
+ LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
+ return ResultInvalidAddress;
+ }
+ if (!IsValidArbitrationType(arb_type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
+ return ResultInvalidEnumValue;
+ }
+
+ // Convert timeout from nanoseconds to ticks.
+ s64 timeout{};
+ if (timeout_ns > 0) {
+ const s64 offset_tick(timeout_ns);
+ if (offset_tick > 0) {
+ timeout = offset_tick + 2;
+ if (timeout <= 0) {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = timeout_ns;
}
- const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
- auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
- const ResultCode result =
- address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
- return result;
+ return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
}
-static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value,
- u32 timeout_low, u32 timeout_high) {
- const auto timeout = static_cast<s64>(timeout_low | (u64{timeout_high} << 32));
- return WaitForAddress(system, address, type, value, timeout);
+static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
+ s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
+ const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
+ return WaitForAddress(system, address, arb_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) {
- LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
- address, type, value, num_to_wake);
+static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
+ s32 value, s32 count) {
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
+ address, signal_type, value, count);
- // If the passed address is a kernel virtual address, return invalid memory state.
- if (Core::Memory::IsKernelVirtualAddress(address)) {
- LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
- return ERR_INVALID_ADDRESS_STATE;
+ // Validate input.
+ if (Memory::IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
+ return ResultInvalidCurrentMemory;
}
-
- // If the address is not properly aligned to 4 bytes, return invalid address.
- if (!Common::IsWordAligned(address)) {
- LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
- return ERR_INVALID_ADDRESS;
+ if (!Common::IsAligned(address, sizeof(s32))) {
+ LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
+ return ResultInvalidAddress;
+ }
+ if (!IsValidSignalType(signal_type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
+ return ResultInvalidEnumValue;
}
- const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
- auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
- return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
+ return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
+ count);
}
-static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value,
- s32 num_to_wake) {
- return SignalToAddress(system, address, type, value, num_to_wake);
+static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
+ s32 value, s32 count) {
+ return SignalToAddress(system, address, signal_type, value, count);
}
static void KernelDebug([[maybe_unused]] Core::System& system,
@@ -1889,20 +1836,28 @@ static ResultCode CloseHandle32(Core::System& system, Handle handle) {
static ResultCode ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
+ // Get the current handle table.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- auto event = handle_table.Get<ReadableEvent>(handle);
- if (event) {
- return event->Reset();
+ // Try to reset as readable event.
+ {
+ auto readable_event = handle_table.Get<KReadableEvent>(handle);
+ if (readable_event) {
+ return readable_event->Reset();
+ }
}
- auto process = handle_table.Get<Process>(handle);
- if (process) {
- return process->ClearSignalState();
+ // Try to reset as process.
+ {
+ auto process = handle_table.Get<Process>(handle);
+ if (process) {
+ return process->Reset();
+ }
}
- LOG_ERROR(Kernel_SVC, "Invalid handle (0x{:08X})", handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
+
+ return ResultInvalidHandle;
}
static ResultCode ResetSignal32(Core::System& system, Handle handle) {
@@ -1918,18 +1873,18 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
if (!Common::Is4KBAligned(addr)) {
LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (!Common::Is4KBAligned(size) || size == 0) {
LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
- return ERR_INVALID_ADDRESS;
+ return ResultInvalidAddress;
}
if (!IsValidAddressRange(addr, size)) {
LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
addr, size);
- return ERR_INVALID_ADDRESS_STATE;
+ return ResultInvalidCurrentMemory;
}
const auto perms{static_cast<Memory::MemoryPermission>(permissions)};
@@ -1937,10 +1892,17 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
perms == Memory::MemoryPermission::Write) {
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
permissions);
- return ERR_INVALID_MEMORY_PERMISSIONS;
+ return ResultInvalidMemoryPermissions;
}
auto& kernel = system.Kernel();
+ // Reserve a new transfer memory from the process resource limit.
+ KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
+ LimitableResource::TransferMemory);
+ if (!trmem_reservation.Succeeded()) {
+ LOG_ERROR(Kernel_SVC, "Could not reserve a new transfer memory");
+ return ResultResourceLimitedExceeded;
+ }
auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
@@ -1952,6 +1914,7 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
if (result.Failed()) {
return result.Code();
}
+ trmem_reservation.Commit();
*handle = *result;
return RESULT_SUCCESS;
@@ -1962,171 +1925,204 @@ static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u
return CreateTransferMemory(system, handle, addr, size, permissions);
}
-static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
- u64* mask) {
+static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
+ u64* out_affinity_mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
+ // Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(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;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle specified (handle={:08X})", thread_handle);
+ return ResultInvalidHandle;
}
- *core = thread->GetIdealCore();
- *mask = thread->GetAffinityMask().GetAffinityMask();
+ // Get the core mask.
+ const auto result = thread->GetCoreMask(out_core_id, out_affinity_mask);
+ if (result.IsError()) {
+ LOG_ERROR(Kernel_SVC, "Unable to successfully retrieve core mask (result={})", result.raw);
+ return result;
+ }
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);
+static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
+ u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
+ u64 out_affinity_mask{};
+ const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
+ *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
+ *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
return result;
}
-static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
+static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
u64 affinity_mask) {
- LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
- thread_handle, core, affinity_mask);
-
- const auto* const current_process = system.Kernel().CurrentProcess();
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core_id=0x{:X}, affinity_mask=0x{:016X}",
+ thread_handle, core_id, affinity_mask);
- if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
- const u8 ideal_cpu_core = current_process->GetIdealCore();
+ const auto& current_process = *system.Kernel().CurrentProcess();
- ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
-
- // Set the target CPU to the ideal core specified by the process.
- core = ideal_cpu_core;
- affinity_mask = 1ULL << core;
+ // Determine the core id/affinity mask.
+ if (core_id == Svc::IdealCoreUseProcessValue) {
+ core_id = current_process.GetIdealCoreId();
+ affinity_mask = (1ULL << core_id);
} else {
- const u64 core_mask = current_process->GetCoreMask();
-
- if ((core_mask | affinity_mask) != core_mask) {
- LOG_ERROR(
- Kernel_SVC,
- "Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
- core_mask, affinity_mask);
- return ERR_INVALID_PROCESSOR_ID;
+ // Validate the affinity mask.
+ const u64 process_core_mask = current_process.GetCoreMask();
+ if ((affinity_mask | process_core_mask) != process_core_mask) {
+ LOG_ERROR(Kernel_SVC,
+ "Affinity mask does match the process core mask (affinity mask={:016X}, core "
+ "mask={:016X})",
+ affinity_mask, process_core_mask);
+ return ResultInvalidCoreId;
}
-
if (affinity_mask == 0) {
- LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
- return ERR_INVALID_COMBINATION;
+ LOG_ERROR(Kernel_SVC, "Affinity mask is zero.");
+ return ResultInvalidCombination;
}
- 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,
- affinity_mask);
- return ERR_INVALID_COMBINATION;
+ // Validate the core id.
+ if (IsValidCoreId(core_id)) {
+ if (((1ULL << core_id) & affinity_mask) == 0) {
+ LOG_ERROR(Kernel_SVC, "Invalid core ID (ID={})", core_id);
+ return ResultInvalidCombination;
+ }
+ } else {
+ if (core_id != IdealCoreNoUpdate && core_id != IdealCoreDontCare) {
+ LOG_ERROR(Kernel_SVC, "Invalid core ID (ID={})", core_id);
+ return ResultInvalidCoreId;
}
- } else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
- core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
- LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
- return ERR_INVALID_PROCESSOR_ID;
}
}
- const auto& handle_table = current_process->GetHandleTable();
- const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ // Get the thread from its handle.
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
- thread_handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Invalid thread handle (handle={:08X})", thread_handle);
+ return ResultInvalidHandle;
}
- return thread->SetCoreAndAffinityMask(core, affinity_mask);
+ // Set the core mask.
+ const auto set_result = thread->SetCoreMask(core_id, affinity_mask);
+ if (set_result.IsError()) {
+ LOG_ERROR(Kernel_SVC, "Unable to successfully set core mask (result={})", set_result.raw);
+ return set_result;
+ }
+ return RESULT_SUCCESS;
}
-static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core,
+static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
u32 affinity_mask_low, u32 affinity_mask_high) {
const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
- return SetThreadCoreMask(system, thread_handle, core, affinity_mask);
+ return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
}
-static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
- LOG_DEBUG(Kernel_SVC, "called");
+static ResultCode SignalEvent(Core::System& system, Handle event_handle) {
+ LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
auto& kernel = system.Kernel();
- const auto [readable_event, writable_event] =
- WritableEvent::CreateEventPair(kernel, "CreateEvent");
+ // Get the current handle table.
+ const HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
- HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
-
- const auto write_create_result = handle_table.Create(writable_event);
- if (write_create_result.Failed()) {
- return write_create_result.Code();
+ // Reserve a new event from the process resource limit.
+ KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
+ LimitableResource::Events);
+ if (!event_reservation.Succeeded()) {
+ LOG_ERROR(Kernel, "Could not reserve a new event");
+ return ResultResourceLimitedExceeded;
}
- *write_handle = *write_create_result;
- const auto read_create_result = handle_table.Create(readable_event);
- if (read_create_result.Failed()) {
- handle_table.Close(*write_create_result);
- return read_create_result.Code();
+ // Get the writable event.
+ auto writable_event = handle_table.Get<KWritableEvent>(event_handle);
+ if (!writable_event) {
+ LOG_ERROR(Kernel_SVC, "Invalid event handle provided (handle={:08X})", event_handle);
+ return ResultInvalidHandle;
}
- *read_handle = *read_create_result;
- LOG_DEBUG(Kernel_SVC,
- "successful. Writable event handle=0x{:08X}, Readable event handle=0x{:08X}",
- *write_create_result, *read_create_result);
- return RESULT_SUCCESS;
+ // Commit the successfuly reservation.
+ event_reservation.Commit();
+
+ return writable_event->Signal();
}
-static ResultCode CreateEvent32(Core::System& system, Handle* write_handle, Handle* read_handle) {
- return CreateEvent(system, write_handle, read_handle);
+static ResultCode SignalEvent32(Core::System& system, Handle event_handle) {
+ return SignalEvent(system, event_handle);
}
-static ResultCode ClearEvent(Core::System& system, Handle handle) {
- LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
+static ResultCode ClearEvent(Core::System& system, Handle event_handle) {
+ LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
+ // Get the current handle table.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- auto writable_event = handle_table.Get<WritableEvent>(handle);
- if (writable_event) {
- writable_event->Clear();
- return RESULT_SUCCESS;
+ // Try to clear the writable event.
+ {
+ auto writable_event = handle_table.Get<KWritableEvent>(event_handle);
+ if (writable_event) {
+ return writable_event->Clear();
+ }
}
- auto readable_event = handle_table.Get<ReadableEvent>(handle);
- if (readable_event) {
- readable_event->Clear();
- return RESULT_SUCCESS;
+ // Try to clear the readable event.
+ {
+ auto readable_event = handle_table.Get<KReadableEvent>(event_handle);
+ if (readable_event) {
+ return readable_event->Clear();
+ }
}
- LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle);
- return ERR_INVALID_HANDLE;
+ LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
+
+ return ResultInvalidHandle;
}
-static ResultCode ClearEvent32(Core::System& system, Handle handle) {
- return ClearEvent(system, handle);
+static ResultCode ClearEvent32(Core::System& system, Handle event_handle) {
+ return ClearEvent(system, event_handle);
}
-static ResultCode SignalEvent(Core::System& system, Handle handle) {
- LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
+static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
+ LOG_DEBUG(Kernel_SVC, "called");
- HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- auto writable_event = handle_table.Get<WritableEvent>(handle);
+ // Get the kernel reference and handle table.
+ auto& kernel = system.Kernel();
+ HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
- if (!writable_event) {
- LOG_ERROR(Kernel_SVC, "Non-existent writable event handle used (0x{:08X})", handle);
- return ERR_INVALID_HANDLE;
+ // Create a new event.
+ const auto event = KEvent::Create(kernel, "CreateEvent");
+ if (!event) {
+ LOG_ERROR(Kernel_SVC, "Unable to create new events. Event creation limit reached.");
+ return ResultOutOfResource;
}
- writable_event->Signal();
+ // Initialize the event.
+ event->Initialize();
+
+ // Add the writable event to the handle table.
+ const auto write_create_result = handle_table.Create(event->GetWritableEvent());
+ if (write_create_result.Failed()) {
+ return write_create_result.Code();
+ }
+ *out_write = *write_create_result;
+
+ // Add the writable event to the handle table.
+ auto handle_guard = SCOPE_GUARD({ handle_table.Close(*write_create_result); });
+
+ // Add the readable event to the handle table.
+ const auto read_create_result = handle_table.Create(event->GetReadableEvent());
+ if (read_create_result.Failed()) {
+ return read_create_result.Code();
+ }
+ *out_read = *read_create_result;
+
+ // We succeeded.
+ handle_guard.Cancel();
return RESULT_SUCCESS;
}
-static ResultCode SignalEvent32(Core::System& system, Handle handle) {
- return SignalEvent(system, handle);
+static ResultCode CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
+ return CreateEvent(system, out_write, out_read);
}
static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
@@ -2142,13 +2138,13 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
if (!process) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
process_handle);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
const auto info_type = static_cast<InfoType>(type);
if (info_type != InfoType::Status) {
LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
- return ERR_INVALID_ENUM_VALUE;
+ return ResultInvalidEnumValue;
}
*out = static_cast<u64>(process->GetStatus());
@@ -2160,7 +2156,7 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle)
LOG_DEBUG(Kernel_SVC, "called");
auto& kernel = system.Kernel();
- auto resource_limit = ResourceLimit::Create(kernel);
+ auto resource_limit = std::make_shared<KResourceLimit>(kernel, system);
auto* const current_process = kernel.CurrentProcess();
ASSERT(current_process != nullptr);
@@ -2207,30 +2203,30 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
resource_type, value);
- const auto type = static_cast<ResourceType>(resource_type);
+ const auto type = static_cast<LimitableResource>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
- return ERR_INVALID_ENUM_VALUE;
+ return ResultInvalidEnumValue;
}
auto* const current_process = system.Kernel().CurrentProcess();
ASSERT(current_process != nullptr);
auto resource_limit_object =
- current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
+ current_process->GetHandleTable().Get<KResourceLimit>(resource_limit);
if (!resource_limit_object) {
LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
resource_limit);
- return ERR_INVALID_HANDLE;
+ return ResultInvalidHandle;
}
const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value));
if (set_result.IsError()) {
- LOG_ERROR(
- Kernel_SVC,
- "Attempted to lower resource limit ({}) for category '{}' below its current value ({})",
- resource_limit_object->GetMaxResourceValue(type), resource_type,
- resource_limit_object->GetCurrentResourceValue(type));
+ LOG_ERROR(Kernel_SVC,
+ "Attempted to lower resource limit ({}) for category '{}' below its current "
+ "value ({})",
+ resource_limit_object->GetLimitValue(type), resource_type,
+ resource_limit_object->GetCurrentValue(type));
return set_result;
}
@@ -2247,7 +2243,7 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
LOG_ERROR(Kernel_SVC,
"Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
out_process_ids_size);
- return ERR_OUT_OF_RANGE;
+ return ResultOutOfRange;
}
const auto& kernel = system.Kernel();
@@ -2257,7 +2253,7 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
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;
+ return ResultInvalidCurrentMemory;
}
auto& memory = system.Memory();
@@ -2286,7 +2282,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
if ((out_thread_ids_size & 0xF0000000) != 0) {
LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
out_thread_ids_size);
- return ERR_OUT_OF_RANGE;
+ return ResultOutOfRange;
}
const auto* const current_process = system.Kernel().CurrentProcess();
@@ -2296,7 +2292,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
!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;
+ return ResultInvalidCurrentMemory;
}
auto& memory = system.Memory();
@@ -2614,7 +2610,7 @@ void Call(Core::System& system, u32 immediate) {
kernel.EnterSVCProfile();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
- thread->SetContinuousOnSVC(true);
+ thread->SetIsCallingSvc();
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
: GetSVCInfo32(immediate);
@@ -2630,7 +2626,7 @@ void Call(Core::System& system, u32 immediate) {
kernel.ExitSVCProfile();
- if (!thread->IsContinuousOnSVC()) {
+ if (!thread->IsCallingSvc()) {
auto* host_context = thread->GetHostContext().get();
host_context->Rewind();
}
diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h
new file mode 100644
index 000000000..4af049551
--- /dev/null
+++ b/src/core/hle/kernel/svc_common.h
@@ -0,0 +1,14 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Kernel::Svc {
+
+constexpr s32 ArgumentHandleCountMax = 0x40;
+constexpr u32 HandleWaitMask{1u << 30};
+
+} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
new file mode 100644
index 000000000..a26d9f2c9
--- /dev/null
+++ b/src/core/hle/kernel/svc_results.h
@@ -0,0 +1,41 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+// Confirmed Switch kernel error codes
+
+constexpr ResultCode ResultMaxConnectionsReached{ErrorModule::Kernel, 7};
+constexpr ResultCode ResultInvalidCapabilityDescriptor{ErrorModule::Kernel, 14};
+constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
+constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
+constexpr ResultCode ResultInvalidSize{ErrorModule::Kernel, 101};
+constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
+constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103};
+constexpr ResultCode ResultOutOfMemory{ErrorModule::Kernel, 104};
+constexpr ResultCode ResultHandleTableFull{ErrorModule::Kernel, 105};
+constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
+constexpr ResultCode ResultInvalidMemoryPermissions{ErrorModule::Kernel, 108};
+constexpr ResultCode ResultInvalidMemoryRange{ErrorModule::Kernel, 110};
+constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112};
+constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113};
+constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
+constexpr ResultCode ResultInvalidPointer{ErrorModule::Kernel, 115};
+constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116};
+constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
+constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
+constexpr ResultCode ResultOutOfRange{ErrorModule::Kernel, 119};
+constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
+constexpr ResultCode ResultNotFound{ErrorModule::Kernel, 121};
+constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
+constexpr ResultCode ResultSessionClosedByRemote{ErrorModule::Kernel, 123};
+constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
+constexpr ResultCode ResultReservedValue{ErrorModule::Kernel, 126};
+constexpr ResultCode ResultResourceLimitedExceeded{ErrorModule::Kernel, 132};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 11e1d8e2d..ec463b97c 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -65,4 +65,34 @@ struct MemoryInfo {
u32 padding{};
};
+enum class SignalType : u32 {
+ Signal = 0,
+ SignalAndIncrementIfEqual = 1,
+ SignalAndModifyByWaitingCountIfEqual = 2,
+};
+
+enum class ArbitrationType : u32 {
+ WaitIfLessThan = 0,
+ DecrementAndWaitIfLessThan = 1,
+ WaitIfEqual = 2,
+};
+
+enum class YieldType : s64 {
+ WithoutCoreMigration = 0,
+ WithCoreMigration = -1,
+ ToAnyThread = -2,
+};
+
+enum class ThreadActivity : u32 {
+ Runnable = 0,
+ Paused = 1,
+};
+
+constexpr inline s32 IdealCoreDontCare = -1;
+constexpr inline s32 IdealCoreUseProcessValue = -2;
+constexpr inline s32 IdealCoreNoUpdate = -3;
+
+constexpr inline s32 LowestThreadPriority = 63;
+constexpr inline s32 HighestThreadPriority = 0;
+
} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 0b6dd9df0..96afd544b 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -7,6 +7,7 @@
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
+#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -57,6 +58,14 @@ void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
}
+// Used by SetThreadActivity
+template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
+void SvcWrap64(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
+ static_cast<Svc::ThreadActivity>(Param(system, 1)))
+ .raw);
+}
+
template <ResultCode func(Core::System&, u32, u64, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
@@ -157,9 +166,18 @@ void SvcWrap64(Core::System& system) {
.raw);
}
-template <ResultCode func(Core::System&, u32, u32*, u64*)>
+// Used by SetThreadCoreMask
+template <ResultCode func(Core::System&, Handle, s32, u64)>
void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
+ static_cast<s32>(Param(system, 1)), Param(system, 2))
+ .raw);
+}
+
+// Used by GetThreadCoreMask
+template <ResultCode func(Core::System&, Handle, s32*, u64*)>
+void SvcWrap64(Core::System& system) {
+ s32 param_1 = 0;
u64 param_2 = 0;
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
@@ -215,9 +233,10 @@ void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
}
-template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
+// Used by WaitSynchronization
+template <ResultCode func(Core::System&, s32*, u64, u64, s64)>
void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
+ s32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
static_cast<s64>(Param(system, 3)))
.raw;
@@ -276,18 +295,22 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64, u32, s32, s64)>
+// Used by WaitForAddress
+template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
- static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
- .raw);
+ FuncReturn(system,
+ func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
+ static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
+ .raw);
}
-template <ResultCode func(Core::System&, u64, u32, s32, s32)>
+// Used by SignalToAddress
+template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)>
void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
- static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
- .raw);
+ FuncReturn(system,
+ func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
+ static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
+ .raw);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -467,12 +490,35 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
+// Used by GetThreadCoreMask32
+template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)>
+void SvcWrap32(Core::System& system) {
+ s32 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 SetThreadActivity32
+template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
+ static_cast<Svc::ThreadActivity>(Param(system, 1)))
+ .raw;
+ FuncReturn(system, retval);
+}
+
// Used by SetThreadPriority32
template <ResultCode func(Core::System&, Handle, u32)>
void SvcWrap32(Core::System& system) {
@@ -481,7 +527,7 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
-// Used by SetThreadCoreMask32
+// Used by SetMemoryAttribute32
template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
@@ -491,6 +537,16 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
+// Used by SetThreadCoreMask32
+template <ResultCode func(Core::System&, Handle, s32, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval =
+ func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(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) {
@@ -503,22 +559,23 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitForAddress32
-template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)>
+template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, 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)))
+ static_cast<Svc::ArbitrationType>(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)>
+template <ResultCode func(Core::System&, u32, Svc::SignalType, 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;
+ const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
+ static_cast<Svc::SignalType>(Param(system, 1)),
+ static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
+ .raw;
FuncReturn(system, retval);
}
@@ -539,9 +596,9 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitSynchronization32
-template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)>
+template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)>
void SvcWrap32(Core::System& system) {
- u32 param_1 = 0;
+ s32 param_1 = 0;
const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
Param32(system, 3), &param_1)
.raw;
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp
deleted file mode 100644
index d3f520ea2..000000000
--- a/src/core/hle/kernel/synchronization.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/core.h"
-#include "core/hle/kernel/errors.h"
-#include "core/hle/kernel/handle_table.h"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
-#include "core/hle/kernel/kernel.h"
-#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 {
-
-Synchronization::Synchronization(Core::System& system) : system{system} {}
-
-void Synchronization::SignalObject(SynchronizationObject& obj) const {
- auto& kernel = system.Kernel();
- KScopedSchedulerLock lock(kernel);
- if (obj.IsSignaled()) {
- 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 = kernel.CurrentScheduler()->GetCurrentThread();
- Handle event_handle = InvalidHandle;
- {
- KScopedSchedulerLockAndSleep 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);
-
- if (event_handle != InvalidHandle) {
- auto& time_manager = kernel.TimeManager();
- time_manager.UnscheduleTimeEvent(event_handle);
- }
-
- {
- KScopedSchedulerLock 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};
- }
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization.h b/src/core/hle/kernel/synchronization.h
deleted file mode 100644
index 379f4b1d3..000000000
--- a/src/core/hle/kernel/synchronization.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "core/hle/kernel/object.h"
-#include "core/hle/result.h"
-
-namespace Core {
-class System;
-} // namespace Core
-
-namespace Kernel {
-
-class SynchronizationObject;
-
-/**
- * The 'Synchronization' class is an interface for handling synchronization methods
- * used by Synchronization objects and synchronization SVCs. This centralizes processing of
- * such
- */
-class Synchronization {
-public:
- explicit Synchronization(Core::System& system);
-
- /// Signals a synchronization object, waking up all its waiting threads
- void SignalObject(SynchronizationObject& obj) const;
-
- /// Tries to see if waiting for any of the sync_objects is necessary, if not
- /// it returns Success and the handle index of the signaled sync object. In
- /// case not, the current thread will be locked and wait for nano_seconds or
- /// for a synchronization object to signal.
- std::pair<ResultCode, Handle> WaitFor(
- std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds);
-
-private:
- Core::System& system;
-};
-} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp
deleted file mode 100644
index ba4d39157..000000000
--- a/src/core/hle/kernel/synchronization_object.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include "common/assert.h"
-#include "common/common_types.h"
-#include "common/logging/log.h"
-#include "core/core.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/synchronization.h"
-#include "core/hle/kernel/synchronization_object.h"
-#include "core/hle/kernel/thread.h"
-
-namespace Kernel {
-
-SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {}
-SynchronizationObject::~SynchronizationObject() = default;
-
-void SynchronizationObject::Signal() {
- kernel.Synchronization().SignalObject(*this);
-}
-
-void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) {
- auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
- if (itr == waiting_threads.end())
- waiting_threads.push_back(std::move(thread));
-}
-
-void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) {
- auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
- // If a thread passed multiple handles to the same object,
- // the kernel might attempt to remove the thread from the object's
- // waiting threads list multiple times.
- if (itr != waiting_threads.end())
- waiting_threads.erase(itr);
-}
-
-void SynchronizationObject::ClearWaitingThreads() {
- waiting_threads.clear();
-}
-
-const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const {
- return waiting_threads;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h
deleted file mode 100644
index 7408ed51f..000000000
--- a/src/core/hle/kernel/synchronization_object.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <atomic>
-#include <memory>
-#include <vector>
-
-#include "core/hle/kernel/object.h"
-
-namespace Kernel {
-
-class KernelCore;
-class Synchronization;
-class Thread;
-
-/// Class that represents a Kernel object that a thread can be waiting on
-class SynchronizationObject : public Object {
-public:
- explicit SynchronizationObject(KernelCore& kernel);
- ~SynchronizationObject() override;
-
- /**
- * Check if the specified thread should wait until the object is available
- * @param thread The thread about which we're deciding.
- * @return True if the current thread should wait due to this object being unavailable
- */
- virtual bool ShouldWait(const Thread* thread) const = 0;
-
- /// Acquire/lock the object for the specified thread if it is available
- virtual void Acquire(Thread* thread) = 0;
-
- /// Signal this object
- virtual void Signal();
-
- virtual bool IsSignaled() const {
- return is_signaled;
- }
-
- /**
- * Add a thread to wait on this object
- * @param thread Pointer to thread to add
- */
- void AddWaitingThread(std::shared_ptr<Thread> thread);
-
- /**
- * Removes a thread from waiting on this object (e.g. if it was resumed already)
- * @param thread Pointer to thread to remove
- */
- void RemoveWaitingThread(std::shared_ptr<Thread> thread);
-
- /// Get a const reference to the waiting threads list for debug use
- const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
-
- void ClearWaitingThreads();
-
-protected:
- std::atomic_bool is_signaled{}; // Tells if this sync object is signaled
-
-private:
- /// Threads waiting for this object to become available
- std::vector<std::shared_ptr<Thread>> waiting_threads;
-};
-
-// Specialization of DynamicObjectCast for SynchronizationObjects
-template <>
-inline std::shared_ptr<SynchronizationObject> DynamicObjectCast<SynchronizationObject>(
- std::shared_ptr<Object> object) {
- if (object != nullptr && object->IsWaitable()) {
- return std::static_pointer_cast<SynchronizationObject>(object);
- }
- return nullptr;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
deleted file mode 100644
index a4f9e0d97..000000000
--- a/src/core/hle/kernel/thread.cpp
+++ /dev/null
@@ -1,478 +0,0 @@
-// Copyright 2014 Citra Emulator Project / PPSSPP Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cinttypes>
-#include <optional>
-#include <vector>
-
-#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/core.h"
-#include "core/cpu_manager.h"
-#include "core/hardware_properties.h"
-#include "core/hle/kernel/errors.h"
-#include "core/hle/kernel/handle_table.h"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/process.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 {
- return status != ThreadStatus::Dead;
-}
-
-bool Thread::IsSignaled() const {
- return status == ThreadStatus::Dead;
-}
-
-void Thread::Acquire(Thread* thread) {
- ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
-}
-
-Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {}
-Thread::~Thread() = default;
-
-void Thread::Stop() {
- {
- KScopedSchedulerLock lock(kernel);
- SetStatus(ThreadStatus::Dead);
- Signal();
- kernel.GlobalHandleTable().Close(global_handle);
-
- if (owner_process) {
- owner_process->UnregisterThread(this);
-
- // Mark the TLS slot in the thread's page as free.
- owner_process->FreeTLSRegion(tls_address);
- }
- has_exited = true;
- }
- global_handle = 0;
-}
-
-void Thread::ResumeFromWait() {
- KScopedSchedulerLock lock(kernel);
- switch (status) {
- case ThreadStatus::Paused:
- case ThreadStatus::WaitSynch:
- case ThreadStatus::WaitHLEEvent:
- case ThreadStatus::WaitSleep:
- case ThreadStatus::WaitIPC:
- 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(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.
- return;
- case ThreadStatus::Dead:
- // This should never happen, as threads must complete before being stopped.
- DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.",
- GetObjectId());
- return;
- }
-
- SetStatus(ThreadStatus::Ready);
-}
-
-void Thread::OnWakeUp() {
- KScopedSchedulerLock lock(kernel);
- SetStatus(ThreadStatus::Ready);
-}
-
-ResultCode Thread::Start() {
- KScopedSchedulerLock lock(kernel);
- SetStatus(ThreadStatus::Ready);
- return RESULT_SUCCESS;
-}
-
-void Thread::CancelWait() {
- KScopedSchedulerLock 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;
- SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
- SetStatus(ThreadStatus::Ready);
-}
-
-static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
- u32 entry_point, u32 arg) {
- context = {};
- context.cpu_registers[0] = arg;
- context.cpu_registers[15] = entry_point;
- context.cpu_registers[13] = stack_top;
-}
-
-static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
- VAddr entry_point, u64 arg) {
- context = {};
- context.cpu_registers[0] = arg;
- context.pc = entry_point;
- context.sp = stack_top;
- // TODO(merry): Perform a hardware test to determine the below value.
- 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(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 && ((type_flags & THREADTYPE_IDLE) == 0)) {
- LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
- return ERR_INVALID_THREAD_PRIORITY;
- }
-
- if (processor_id > THREADPROCESSORID_MAX) {
- LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id);
- return ERR_INVALID_PROCESSOR_ID;
- }
-
- 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);
-
- thread->thread_id = kernel.CreateNewThreadID();
- thread->status = ThreadStatus::Dormant;
- thread->entry_point = entry_point;
- thread->stack_top = stack_top;
- thread->disable_count = 1;
- thread->tpidr_el0 = 0;
- thread->nominal_priority = thread->current_priority = priority;
- thread->schedule_count = -1;
- thread->last_scheduled_tick = 0;
- thread->processor_id = processor_id;
- thread->ideal_core = processor_id;
- thread->affinity_mask.SetAffinity(processor_id, true);
- 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;
- thread->type = type_flags;
- if ((type_flags & THREADTYPE_IDLE) == 0) {
- auto& scheduler = kernel.GlobalSchedulerContext();
- scheduler.AddThread(thread);
- }
- if (owner_process) {
- thread->tls_address = thread->owner_process->CreateTLSRegion();
- thread->owner_process->RegisterThread(thread.get());
- } else {
- thread->tls_address = 0;
- }
-
- // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
- // to initialize the context
- if ((type_flags & THREADTYPE_HLE) == 0) {
- 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) {
- KScopedSchedulerLock lock(kernel);
- ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
- "Invalid priority value.");
- nominal_priority = priority;
- UpdatePriority();
-}
-
-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);
-}
-
-VAddr Thread::GetCommandBufferAddress() const {
- // Offset from the start of TLS at which the IPC command buffer begins.
- constexpr u64 command_header_offset = 0x80;
- return GetTLSAddress() + command_header_offset;
-}
-
-void Thread::SetStatus(ThreadStatus new_status) {
- if (new_status == status) {
- return;
- }
-
- switch (new_status) {
- case ThreadStatus::Ready:
- SetSchedulingStatus(ThreadSchedStatus::Runnable);
- break;
- case ThreadStatus::Dormant:
- SetSchedulingStatus(ThreadSchedStatus::None);
- break;
- case ThreadStatus::Dead:
- SetSchedulingStatus(ThreadSchedStatus::Exited);
- break;
- default:
- SetSchedulingStatus(ThreadSchedStatus::Paused);
- break;
- }
-
- status = new_status;
-}
-
-void Thread::AddMutexWaiter(std::shared_ptr<Thread> thread) {
- if (thread->lock_owner.get() == this) {
- // If the thread is already waiting for this thread to release the mutex, ensure that the
- // waiters list is consistent and return without doing anything.
- const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(iter != wait_mutex_threads.end());
- return;
- }
-
- // A thread can't wait on two different mutexes at the same time.
- ASSERT(thread->lock_owner == nullptr);
-
- // Ensure that the thread is not already in the list of mutex waiters
- const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(iter == wait_mutex_threads.end());
-
- // Keep the list in an ordered fashion
- const auto insertion_point = std::find_if(
- wait_mutex_threads.begin(), wait_mutex_threads.end(),
- [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
- wait_mutex_threads.insert(insertion_point, thread);
- thread->lock_owner = SharedFrom(this);
-
- UpdatePriority();
-}
-
-void Thread::RemoveMutexWaiter(std::shared_ptr<Thread> thread) {
- ASSERT(thread->lock_owner.get() == this);
-
- // Ensure that the thread is in the list of mutex waiters
- const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(iter != wait_mutex_threads.end());
-
- wait_mutex_threads.erase(iter);
-
- thread->lock_owner = nullptr;
- UpdatePriority();
-}
-
-void Thread::UpdatePriority() {
- // If any of the threads waiting on the mutex have a higher priority
- // (taking into account priority inheritance), then this thread inherits
- // that thread's priority.
- u32 new_priority = nominal_priority;
- if (!wait_mutex_threads.empty()) {
- if (wait_mutex_threads.front()->current_priority < new_priority) {
- new_priority = wait_mutex_threads.front()->current_priority;
- }
- }
-
- if (new_priority == current_priority) {
- return;
- }
-
- if (GetStatus() == ThreadStatus::WaitCondVar) {
- owner_process->RemoveConditionVariableThread(SharedFrom(this));
- }
-
- SetCurrentPriority(new_priority);
-
- if (GetStatus() == ThreadStatus::WaitCondVar) {
- owner_process->InsertConditionVariableThread(SharedFrom(this));
- }
-
- if (!lock_owner) {
- return;
- }
-
- // Ensure that the thread is within the correct location in the waiting list.
- auto old_owner = lock_owner;
- lock_owner->RemoveMutexWaiter(SharedFrom(this));
- old_owner->AddMutexWaiter(SharedFrom(this));
-
- // Recursively update the priority of the thread that depends on the priority of this one.
- lock_owner->UpdatePriority();
-}
-
-bool Thread::AllSynchronizationObjectsReady() const {
- return std::none_of(wait_objects->begin(), wait_objects->end(),
- [this](const std::shared_ptr<SynchronizationObject>& object) {
- return object->ShouldWait(this);
- });
-}
-
-bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) {
- ASSERT(hle_callback);
- return hle_callback(std::move(thread));
-}
-
-ResultCode Thread::SetActivity(ThreadActivity value) {
- KScopedSchedulerLock 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) {
- 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;
- }
- RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
- }
- return RESULT_SUCCESS;
-}
-
-ResultCode Thread::Sleep(s64 nanoseconds) {
- Handle event_handle{};
- {
- KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds);
- SetStatus(ThreadStatus::WaitSleep);
- }
-
- if (event_handle != InvalidHandle) {
- auto& time_manager = kernel.TimeManager();
- time_manager.UnscheduleTimeEvent(event_handle);
- }
- return RESULT_SUCCESS;
-}
-
-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;
- KScheduler::OnThreadStateChanged(kernel, this, old_state);
-}
-
-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;
- KScheduler::OnThreadStateChanged(kernel, this, old_state);
-}
-
-void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
- const u32 old_state = scheduling_state;
- scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) |
- static_cast<u32>(new_status);
- KScheduler::OnThreadStateChanged(kernel, this, old_state);
-}
-
-void Thread::SetCurrentPriority(u32 new_priority) {
- const u32 old_priority = std::exchange(current_priority, new_priority);
- KScheduler::OnThreadPriorityChanged(kernel, this, kernel.CurrentScheduler()->GetCurrentThread(),
- old_priority);
-}
-
-ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
- KScopedSchedulerLock 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) {
- return core;
- }
- }
- return -1;
- };
-
- const bool use_override = affinity_override_count != 0;
- 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;
- }
- }
- if (use_override) {
- ideal_core_override = new_core;
- } else {
- const auto old_affinity_mask = affinity_mask;
- affinity_mask.SetAffinityMask(new_affinity_mask);
- ideal_core = new_core;
- if (old_affinity_mask.GetAffinityMask() != new_affinity_mask) {
- const s32 old_core = processor_id;
- if (processor_id >= 0 && !affinity_mask.GetAffinity(processor_id)) {
- if (static_cast<s32>(ideal_core) < 0) {
- processor_id = HighestSetCore(affinity_mask.GetAffinityMask(),
- Core::Hardware::NUM_CPU_CORES);
- } else {
- processor_id = ideal_core;
- }
- }
- KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_affinity_mask, old_core);
- }
- }
- return RESULT_SUCCESS;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
deleted file mode 100644
index 11ef29888..000000000
--- a/src/core/hle/kernel/thread.h
+++ /dev/null
@@ -1,731 +0,0 @@
-// Copyright 2014 Citra Emulator Project / PPSSPP Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#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/k_affinity_mask.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 GlobalSchedulerContext;
-class KernelCore;
-class Process;
-class KScheduler;
-
-enum ThreadPriority : u32 {
- 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 {
- /// Indicates that no particular processor core is preferred.
- THREADPROCESSORID_DONT_CARE = -1,
-
- /// Run thread on the ideal core specified by the process.
- THREADPROCESSORID_IDEAL = -2,
-
- /// Indicates that the preferred processor ID shouldn't be updated in
- /// a core mask setting operation.
- THREADPROCESSORID_DONT_UPDATE = -3,
-
- THREADPROCESSORID_0 = 0, ///< Run thread on core 0
- THREADPROCESSORID_1 = 1, ///< Run thread on core 1
- THREADPROCESSORID_2 = 2, ///< Run thread on core 2
- THREADPROCESSORID_3 = 3, ///< Run thread on core 3
- THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
-
- /// Allowed CPU mask
- THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
- (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3)
-};
-
-enum class ThreadStatus {
- Ready, ///< Ready to run
- Paused, ///< Paused by SetThreadActivity or debug
- WaitHLEEvent, ///< Waiting for hle event to finish
- WaitSleep, ///< Waiting due to a SleepThread SVC
- WaitIPC, ///< Waiting for the reply from an IPC request
- WaitSynch, ///< Waiting due to WaitSynchronization
- WaitMutex, ///< Waiting due to an ArbitrateLock svc
- WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
- WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
- Dormant, ///< Created but not yet made ready
- Dead ///< Run to completion, or forcefully terminated
-};
-
-enum class ThreadWakeupReason {
- Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
- Timeout // The thread was woken up due to a wait timeout.
-};
-
-enum class ThreadActivity : u32 {
- Normal = 0,
- Paused = 1,
-};
-
-enum class ThreadSchedStatus : u32 {
- None = 0,
- Paused = 1,
- Runnable = 2,
- Exited = 3,
-};
-
-enum class ThreadSchedFlags : u32 {
- ProcessPauseFlag = 1 << 4,
- ThreadPauseFlag = 1 << 5,
- ProcessDebugPauseFlag = 1 << 6,
- KernelInitPauseFlag = 1 << 8,
-};
-
-enum class ThreadSchedMasks : u32 {
- LowMask = 0x000f,
- HighMask = 0xfff0,
- ForcePauseMask = 0x0070,
-};
-
-class Thread final : public SynchronizationObject {
-public:
- explicit Thread(KernelCore& kernel);
- ~Thread() override;
-
- using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>;
-
- using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
- using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
-
- using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>;
-
- 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 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
- * @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(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;
- }
-
- void SetName(std::string new_name) {
- name = std::move(new_name);
- }
-
- std::string GetTypeName() const override {
- return "Thread";
- }
-
- static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
-
- bool ShouldWait(const Thread* thread) const override;
- void Acquire(Thread* thread) override;
- bool IsSignaled() const override;
-
- /**
- * Gets the thread's current priority
- * @return The current thread's priority
- */
- u32 GetPriority() const {
- return current_priority;
- }
-
- /**
- * Gets the thread's nominal priority.
- * @return The current thread's nominal priority.
- */
- u32 GetNominalPriority() const {
- return nominal_priority;
- }
-
- /**
- * Sets the thread's current priority
- * @param priority The new priority
- */
- void SetPriority(u32 priority);
-
- /// Adds a thread to the list of threads that are waiting for a lock held by this thread.
- void AddMutexWaiter(std::shared_ptr<Thread> thread);
-
- /// Removes a thread from the list of threads that are waiting for a lock held by this thread.
- void RemoveMutexWaiter(std::shared_ptr<Thread> thread);
-
- /// Recalculates the current priority taking into account priority inheritance.
- void UpdatePriority();
-
- /// Changes the core that the thread is running or scheduled to run on.
- ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
-
- /**
- * Gets the thread's thread ID
- * @return The thread's ID
- */
- u64 GetThreadID() const {
- return thread_id;
- }
-
- /// 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
- /// waiting result to signal a canceled wait. The function will then resume
- /// this thread.
- ///
- void CancelWait();
-
- void SetSynchronizationResults(SynchronizationObject* object, ResultCode result);
-
- 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
- * that the thread passed to WaitSynchronization, starting the search from the last element.
- *
- * It is used to set the output index of WaitSynchronization when the thread is awakened.
- *
- * When a thread wakes up due to an object signal, the kernel will use the index of the last
- * matching object in the wait objects list in case of having multiple instances of the same
- * object in the list.
- *
- * @param object Object to query the index of.
- */
- s32 GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const;
-
- /**
- * Stops a thread, invalidating it from further use
- */
- void Stop();
-
- /*
- * Returns the Thread Local Storage address of the current thread
- * @returns VAddr of the thread's TLS
- */
- VAddr GetTLSAddress() const {
- return tls_address;
- }
-
- /*
- * Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
- * @returns The value of the TPIDR_EL0 register.
- */
- u64 GetTPIDR_EL0() const {
- return tpidr_el0;
- }
-
- /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
- void SetTPIDR_EL0(u64 value) {
- tpidr_el0 = value;
- }
-
- /*
- * Returns the address of the current thread's command buffer, located in the TLS.
- * @returns VAddr of the thread's command buffer.
- */
- VAddr GetCommandBufferAddress() const;
-
- ThreadContext32& GetContext32() {
- return context_32;
- }
-
- const ThreadContext32& GetContext32() const {
- return context_32;
- }
-
- ThreadContext64& GetContext64() {
- return context_64;
- }
-
- const ThreadContext64& GetContext64() const {
- 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;
- }
-
- void SetStatus(ThreadStatus new_status);
-
- s64 GetLastScheduledTick() const {
- return this->last_scheduled_tick;
- }
-
- void SetLastScheduledTick(s64 tick) {
- this->last_scheduled_tick = tick;
- }
-
- u64 GetTotalCPUTimeTicks() const {
- return total_cpu_time_ticks;
- }
-
- void UpdateCPUTimeTicks(u64 ticks) {
- total_cpu_time_ticks += ticks;
- }
-
- s32 GetProcessorID() const {
- return processor_id;
- }
-
- s32 GetActiveCore() const {
- return GetProcessorID();
- }
-
- void SetProcessorID(s32 new_core) {
- processor_id = new_core;
- }
-
- void SetActiveCore(s32 new_core) {
- processor_id = new_core;
- }
-
- Process* GetOwnerProcess() {
- return owner_process;
- }
-
- const Process* GetOwnerProcess() const {
- return owner_process;
- }
-
- const ThreadSynchronizationObjects& GetSynchronizationObjects() const {
- return *wait_objects;
- }
-
- void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) {
- wait_objects = objects;
- }
-
- void ClearSynchronizationObjects() {
- for (const auto& waiting_object : *wait_objects) {
- waiting_object->RemoveWaitingThread(SharedFrom(this));
- }
- wait_objects->clear();
- }
-
- /// Determines whether all the objects this thread is waiting on are ready.
- bool AllSynchronizationObjectsReady() const;
-
- const MutexWaitingThreads& GetMutexWaitingThreads() const {
- return wait_mutex_threads;
- }
-
- Thread* GetLockOwner() const {
- return lock_owner.get();
- }
-
- void SetLockOwner(std::shared_ptr<Thread> owner) {
- lock_owner = std::move(owner);
- }
-
- VAddr GetCondVarWaitAddress() const {
- return condvar_wait_address;
- }
-
- void SetCondVarWaitAddress(VAddr address) {
- condvar_wait_address = address;
- }
-
- VAddr GetMutexWaitAddress() const {
- return mutex_wait_address;
- }
-
- void SetMutexWaitAddress(VAddr address) {
- mutex_wait_address = address;
- }
-
- Handle GetWaitHandle() const {
- return wait_handle;
- }
-
- void SetWaitHandle(Handle handle) {
- wait_handle = handle;
- }
-
- VAddr GetArbiterWaitAddress() const {
- return arb_wait_address;
- }
-
- void SetArbiterWaitAddress(VAddr address) {
- arb_wait_address = address;
- }
-
- bool HasHLECallback() const {
- return hle_callback != nullptr;
- }
-
- void SetHLECallback(HLECallback callback) {
- hle_callback = std::move(callback);
- }
-
- void SetHLETimeEvent(Handle time_event) {
- hle_time_event = time_event;
- }
-
- 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;
- }
-
- const KAffinityMask& GetAffinityMask() const {
- return affinity_mask;
- }
-
- ResultCode SetActivity(ThreadActivity value);
-
- /// Sleeps this thread for the given amount of nanoseconds.
- ResultCode Sleep(s64 nanoseconds);
-
- s64 GetYieldScheduleCount() const {
- return this->schedule_count;
- }
-
- void SetYieldScheduleCount(s64 count) {
- this->schedule_count = count;
- }
-
- ThreadSchedStatus GetSchedulingStatus() const {
- return static_cast<ThreadSchedStatus>(scheduling_state &
- static_cast<u32>(ThreadSchedMasks::LowMask));
- }
-
- bool IsRunnable() const {
- return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable);
- }
-
- bool IsRunning() const {
- return is_running;
- }
-
- void SetIsRunning(bool value) {
- is_running = value;
- }
-
- bool IsSyncCancelled() const {
- return is_sync_cancelled;
- }
-
- void SetSyncCancelled(bool value) {
- is_sync_cancelled = value;
- }
-
- Handle GetGlobalHandle() const {
- 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;
- }
-
- class QueueEntry {
- public:
- constexpr QueueEntry() = default;
-
- constexpr void Initialize() {
- this->prev = nullptr;
- this->next = nullptr;
- }
-
- constexpr Thread* GetPrev() const {
- return this->prev;
- }
- constexpr Thread* GetNext() const {
- return this->next;
- }
- constexpr void SetPrev(Thread* thread) {
- this->prev = thread;
- }
- constexpr void SetNext(Thread* thread) {
- this->next = thread;
- }
-
- private:
- Thread* prev{};
- Thread* next{};
- };
-
- QueueEntry& GetPriorityQueueEntry(s32 core) {
- return this->per_core_priority_queue_entry[core];
- }
-
- const QueueEntry& GetPriorityQueueEntry(s32 core) const {
- return this->per_core_priority_queue_entry[core];
- }
-
- s32 GetDisableDispatchCount() const {
- return disable_count;
- }
-
- void DisableDispatch() {
- ASSERT(GetDisableDispatchCount() >= 0);
- disable_count++;
- }
-
- void EnableDispatch() {
- ASSERT(GetDisableDispatchCount() > 0);
- disable_count--;
- }
-
-private:
- friend class GlobalSchedulerContext;
- friend class KScheduler;
- friend class Process;
-
- void SetSchedulingStatus(ThreadSchedStatus new_status);
- void AddSchedulingFlag(ThreadSchedFlags flag);
- void RemoveSchedulingFlag(ThreadSchedFlags flag);
-
- void SetCurrentPriority(u32 new_priority);
-
- Common::SpinLock context_guard{};
- ThreadContext32 context_32{};
- ThreadContext64 context_64{};
- std::shared_ptr<Common::Fiber> host_context{};
-
- ThreadStatus status = ThreadStatus::Dormant;
- u32 scheduling_state = 0;
-
- u64 thread_id = 0;
-
- VAddr entry_point = 0;
- VAddr stack_top = 0;
- std::atomic_int disable_count = 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.
- u32 nominal_priority = 0;
-
- /// Current thread priority. This may change over the course of the
- /// thread's lifetime in order to facilitate priority inheritance.
- u32 current_priority = 0;
-
- u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
- s64 schedule_count{};
- s64 last_scheduled_tick{};
-
- s32 processor_id = 0;
-
- VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread
- u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
-
- /// Process that owns this thread
- Process* owner_process;
-
- /// Objects that the thread is waiting on, in the same order as they were
- /// passed to WaitSynchronization.
- 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;
-
- /// Thread that owns the lock that this thread is waiting for.
- std::shared_ptr<Thread> lock_owner;
-
- /// If waiting on a ConditionVariable, this is the ConditionVariable address
- VAddr condvar_wait_address = 0;
- /// If waiting on a Mutex, this is the mutex address
- VAddr mutex_wait_address = 0;
- /// The handle used to wait for the mutex.
- Handle wait_handle = 0;
-
- /// 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 for HLE Events
- HLECallback hle_callback;
- Handle hle_time_event;
- SynchronizationObject* hle_object;
-
- KScheduler* scheduler = nullptr;
-
- std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
-
- u32 ideal_core{0xFFFFFFFF};
- KAffinityMask affinity_mask{};
-
- s32 ideal_core_override = -1;
- u32 affinity_override_count = 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;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 79628e2b4..fd0630019 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -8,8 +8,8 @@
#include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
@@ -18,53 +18,30 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
time_manager_event_type = Core::Timing::CreateEvent(
"Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
- const KScopedSchedulerLock lock(system.Kernel());
- const auto proper_handle = static_cast<Handle>(thread_handle);
-
- std::shared_ptr<Thread> thread;
+ std::shared_ptr<KThread> thread;
{
std::lock_guard lock{mutex};
- if (cancelled_events[proper_handle]) {
- return;
- }
- thread = system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
- }
-
- if (thread) {
- // Thread can be null if process has exited
- thread->OnWakeUp();
+ thread = SharedFrom<KThread>(reinterpret_cast<KThread*>(thread_handle));
}
+ thread->Wakeup();
});
}
-void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
+void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
std::lock_guard lock{mutex};
- event_handle = timetask->GetGlobalHandle();
if (nanoseconds > 0) {
- ASSERT(timetask);
- ASSERT(timetask->GetStatus() != ThreadStatus::Ready);
- ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex);
+ ASSERT(thread);
+ ASSERT(thread->GetState() != ThreadState::Runnable);
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) {
- std::lock_guard lock{mutex};
- if (event_handle == InvalidHandle) {
- return;
+ time_manager_event_type,
+ reinterpret_cast<uintptr_t>(thread));
}
- system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
- cancelled_events[event_handle] = true;
}
-void TimeManager::CancelTimeEvent(Thread* time_task) {
+void TimeManager::UnscheduleTimeEvent(KThread* thread) {
std::lock_guard lock{mutex};
- const Handle event_handle = time_task->GetGlobalHandle();
- UnscheduleTimeEvent(event_handle);
+ system.CoreTiming().UnscheduleEvent(time_manager_event_type,
+ reinterpret_cast<uintptr_t>(thread));
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
index f39df39a0..0d7f05f30 100644
--- a/src/core/hle/kernel/time_manager.h
+++ b/src/core/hle/kernel/time_manager.h
@@ -20,7 +20,7 @@ struct EventType;
namespace Kernel {
-class Thread;
+class KThread;
/**
* The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
@@ -31,18 +31,14 @@ public:
explicit TimeManager(Core::System& system);
/// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
- /// returns a non-invalid handle in `event_handle` if correctly scheduled
- void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds);
+ void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds);
/// Unschedule an existing time event
- void UnscheduleTimeEvent(Handle event_handle);
-
- void CancelTimeEvent(Thread* time_task);
+ void UnscheduleTimeEvent(KThread* thread);
private:
Core::System& system;
std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
- std::unordered_map<Handle, bool> cancelled_events;
std::mutex mutex;
};
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
index 765f408c3..6b0fc1591 100644
--- a/src/core/hle/kernel/transfer_memory.cpp
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
@@ -17,6 +18,7 @@ TransferMemory::TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory)
TransferMemory::~TransferMemory() {
// Release memory region when transfer memory is destroyed
Reset();
+ owner_process->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1);
}
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel,
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
index 05e9f7464..777799d12 100644
--- a/src/core/hle/kernel/transfer_memory.h
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -72,6 +72,8 @@ public:
/// is closed.
ResultCode Reset();
+ void Finalize() override {}
+
private:
/// The base address for the memory managed by this instance.
VAddr base_address{};
diff --git a/src/core/hle/kernel/writable_event.cpp b/src/core/hle/kernel/writable_event.cpp
deleted file mode 100644
index fc2f7c424..000000000
--- a/src/core/hle/kernel/writable_event.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include "common/assert.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/writable_event.h"
-
-namespace Kernel {
-
-WritableEvent::WritableEvent(KernelCore& kernel) : Object{kernel} {}
-WritableEvent::~WritableEvent() = default;
-
-EventPair WritableEvent::CreateEventPair(KernelCore& kernel, std::string name) {
- std::shared_ptr<WritableEvent> writable_event(new WritableEvent(kernel));
- std::shared_ptr<ReadableEvent> readable_event(new ReadableEvent(kernel));
-
- writable_event->name = name + ":Writable";
- writable_event->readable = readable_event;
- readable_event->name = name + ":Readable";
-
- return {std::move(readable_event), std::move(writable_event)};
-}
-
-std::shared_ptr<ReadableEvent> WritableEvent::GetReadableEvent() const {
- return readable;
-}
-
-void WritableEvent::Signal() {
- readable->Signal();
-}
-
-void WritableEvent::Clear() {
- readable->Clear();
-}
-
-bool WritableEvent::IsSignaled() const {
- return readable->IsSignaled();
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h
deleted file mode 100644
index 6189cf65c..000000000
--- a/src/core/hle/kernel/writable_event.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-
-#include "core/hle/kernel/object.h"
-
-namespace Kernel {
-
-class KernelCore;
-class ReadableEvent;
-class WritableEvent;
-
-struct EventPair {
- std::shared_ptr<ReadableEvent> readable;
- std::shared_ptr<WritableEvent> writable;
-};
-
-class WritableEvent final : public Object {
-public:
- ~WritableEvent() override;
-
- /**
- * Creates an event
- * @param kernel The kernel instance to create this event under.
- * @param name Optional name of event
- */
- static EventPair CreateEventPair(KernelCore& kernel, std::string name = "Unknown");
-
- std::string GetTypeName() const override {
- return "WritableEvent";
- }
- std::string GetName() const override {
- return name;
- }
-
- static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
-
- std::shared_ptr<ReadableEvent> GetReadableEvent() const;
-
- void Signal();
- void Clear();
- bool IsSignaled() const;
-
-private:
- explicit WritableEvent(KernelCore& kernel);
-
- std::shared_ptr<ReadableEvent> readable;
-
- std::string name; ///< Name of event (optional)
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 6981f8ee7..3ec0e1eca 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -32,9 +32,15 @@
namespace Service::Account {
-constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
+constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20};
+constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22};
+constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30};
+constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31};
constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
+// Thumbnails are hard coded to be at least this size
+constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
+
static std::string GetImagePath(Common::UUID uuid) {
return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
@@ -369,7 +375,7 @@ protected:
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_INVALID_BUFFER_SIZE);
+ rb.Push(ERR_INVALID_BUFFER);
return;
}
@@ -402,7 +408,7 @@ protected:
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_INVALID_BUFFER_SIZE);
+ rb.Push(ERR_INVALID_BUFFER);
return;
}
@@ -534,7 +540,7 @@ private:
rb.Push(RESULT_SUCCESS);
}
- Common::UUID user_id;
+ Common::UUID user_id{Common::INVALID_UUID};
};
// 6.0.0+
@@ -811,6 +817,55 @@ void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ct
rb.Push(RESULT_SUCCESS);
}
+void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid = rp.PopRaw<Common::UUID>();
+
+ LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format());
+
+ // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
+ // way of confirming things like the TID, we're going to assume a non zero value for the time
+ // being.
+ constexpr u64 tid{1};
+ StoreSaveDataThumbnail(ctx, uuid, tid);
+}
+
+void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid = rp.PopRaw<Common::UUID>();
+ const auto tid = rp.Pop<u64_le>();
+
+ LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid);
+ StoreSaveDataThumbnail(ctx, uuid, tid);
+}
+
+void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx,
+ const Common::UUID& uuid, const u64 tid) {
+ IPC::ResponseBuilder rb{ctx, 2};
+
+ if (tid == 0) {
+ LOG_ERROR(Service_ACC, "TitleID is not valid!");
+ rb.Push(ERR_INVALID_APPLICATION_ID);
+ return;
+ }
+
+ if (!uuid) {
+ LOG_ERROR(Service_ACC, "User ID is not valid!");
+ rb.Push(ERR_INVALID_USER_ID);
+ return;
+ }
+ const auto thumbnail_size = ctx.GetReadBufferSize();
+ if (thumbnail_size != THUMBNAIL_SIZE) {
+ LOG_ERROR(Service_ACC, "Buffer size is empty! size={:X} expecting {:X}", thumbnail_size,
+ THUMBNAIL_SIZE);
+ rb.Push(ERR_INVALID_BUFFER_SIZE);
+ return;
+ }
+
+ // TODO(ogniK): Construct save data thumbnail
+ 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 ab8edc049..0e3ad8ec6 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -4,6 +4,7 @@
#pragma once
+#include "common/uuid.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/service.h"
@@ -36,9 +37,13 @@ public:
void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
void LoadOpenContext(Kernel::HLERequestContext& ctx);
void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
+ void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx);
+ void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx);
private:
ResultCode InitializeApplicationInfoBase();
+ void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid,
+ const u64 tid);
enum class ApplicationType : u32_le {
GameCard = 0,
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index d2bb8c2c8..49b22583e 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -29,7 +29,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{104, nullptr, "GetProfileUpdateNotifier"},
{105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
{106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
- {110, nullptr, "StoreSaveDataThumbnail"},
+ {110, &ACC_SU::StoreSaveDataThumbnailSystem, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{112, nullptr, "LoadSaveDataThumbnail"},
{113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 75a24f8f5..8d66d180d 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -26,7 +26,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
{102, nullptr, "AuthenticateApplicationAsync"},
{103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
- {110, nullptr, "StoreSaveDataThumbnail"},
+ {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{120, nullptr, "CreateGuestLoginRequest"},
{130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index a4aa5316a..951081cd0 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -29,7 +29,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{104, nullptr, "GetProfileUpdateNotifier"},
{105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
{106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
- {110, nullptr, "StoreSaveDataThumbnail"},
+ {110, &ACC_U1::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{112, nullptr, "LoadSaveDataThumbnail"},
{113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 9b829e957..50b2c58e2 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -41,12 +41,18 @@ constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/
ProfileManager::ProfileManager() {
ParseUserSaveFile();
- if (user_count == 0)
+ // Create an user if none are present
+ if (user_count == 0) {
CreateNewUser(UUID::Generate(), "yuzu");
+ }
auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
- if (UserExistsIndex(current))
+
+ // If user index don't exist. Load the first user and change the active user
+ if (!UserExistsIndex(current)) {
current = 0;
+ Settings::values.current_user = 0;
+ }
OpenUser(*GetUser(current));
}
@@ -227,17 +233,17 @@ void ProfileManager::CloseUser(UUID uuid) {
/// Gets all valid user ids on the system
UserIDArray ProfileManager::GetAllUsers() const {
- UserIDArray output;
- std::transform(profiles.begin(), profiles.end(), output.begin(),
- [](const ProfileInfo& p) { return p.user_uuid; });
+ UserIDArray output{};
+ std::ranges::transform(profiles, output.begin(),
+ [](const ProfileInfo& p) { return p.user_uuid; });
return output;
}
/// Get all the open users on the system and zero out the rest of the data. This is specifically
/// needed for GetOpenUsers and we need to ensure the rest of the output buffer is zero'd out
UserIDArray ProfileManager::GetOpenUsers() const {
- UserIDArray output;
- std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
+ UserIDArray output{};
+ std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) {
if (p.is_open)
return p.user_uuid;
return UUID{Common::INVALID_UUID};
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 5310637a6..71b9d5518 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -23,12 +23,12 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
struct ProfileData {
- INSERT_PADDING_WORDS(1);
- u32 icon_id{};
- u8 bg_color_id{};
- INSERT_PADDING_BYTES(0x7);
- INSERT_PADDING_BYTES(0x10);
- INSERT_PADDING_BYTES(0x60);
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u32 icon_id;
+ u8 bg_color_id;
+ INSERT_PADDING_BYTES_NOINIT(0x7);
+ INSERT_PADDING_BYTES_NOINIT(0x10);
+ INSERT_PADDING_BYTES_NOINIT(0x60);
};
static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
@@ -43,9 +43,9 @@ struct ProfileInfo {
};
struct ProfileBase {
- Common::UUID user_uuid{Common::INVALID_UUID};
- u64_le timestamp{};
- ProfileUsername username{};
+ Common::UUID user_uuid;
+ u64_le timestamp;
+ ProfileUsername username;
// Zero out all the fields to make the profile slot considered "Empty"
void Invalidate() {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index c9808060a..8e1fe9438 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -13,11 +13,12 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/transfer_memory.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
@@ -303,17 +304,18 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- launchable_event =
- Kernel::WritableEvent::CreateEventPair(kernel, "ISelfController:LaunchableEvent");
+ launchable_event = Kernel::KEvent::Create(kernel, "ISelfController:LaunchableEvent");
+ launchable_event->Initialize();
// This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
// called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
// ISelfControllers. The event is signaled on creation, and on transition from suspended -> not
// suspended if the event has previously been created by a call to
// GetAccumulatedSuspendedTickChangedEvent.
- accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
- kernel, "ISelfController:AccumulatedSuspendedTickChangedEvent");
- accumulated_suspended_tick_changed_event.writable->Signal();
+ accumulated_suspended_tick_changed_event =
+ Kernel::KEvent::Create(kernel, "ISelfController:AccumulatedSuspendedTickChangedEvent");
+ accumulated_suspended_tick_changed_event->Initialize();
+ accumulated_suspended_tick_changed_event->GetWritableEvent()->Signal();
}
ISelfController::~ISelfController() = default;
@@ -372,11 +374,11 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
- launchable_event.writable->Signal();
+ launchable_event->GetWritableEvent()->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(launchable_event.readable);
+ rb.PushCopyObjects(launchable_event->GetReadableEvent());
}
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
@@ -555,41 +557,42 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
+ rb.PushCopyObjects(accumulated_suspended_tick_changed_event->GetReadableEvent());
}
AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
- on_new_message =
- Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageReceived");
+ on_new_message = Kernel::KEvent::Create(kernel, "AMMessageQueue:OnMessageReceived");
+ on_new_message->Initialize();
on_operation_mode_changed =
- Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged");
+ Kernel::KEvent::Create(kernel, "AMMessageQueue:OperationModeChanged");
+ on_operation_mode_changed->Initialize();
}
AppletMessageQueue::~AppletMessageQueue() = default;
-const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const {
- return on_new_message.readable;
+const std::shared_ptr<Kernel::KReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const {
+ return on_new_message->GetReadableEvent();
}
-const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetOperationModeChangedEvent()
+const std::shared_ptr<Kernel::KReadableEvent>& AppletMessageQueue::GetOperationModeChangedEvent()
const {
- return on_operation_mode_changed.readable;
+ return on_operation_mode_changed->GetReadableEvent();
}
void AppletMessageQueue::PushMessage(AppletMessage msg) {
messages.push(msg);
- on_new_message.writable->Signal();
+ on_new_message->GetWritableEvent()->Signal();
}
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
if (messages.empty()) {
- on_new_message.writable->Clear();
+ on_new_message->GetWritableEvent()->Clear();
return AppletMessage::NoMessage;
}
auto msg = messages.front();
messages.pop();
if (messages.empty()) {
- on_new_message.writable->Clear();
+ on_new_message->GetWritableEvent()->Clear();
}
return msg;
}
@@ -601,7 +604,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
void AppletMessageQueue::OperationModeChanged() {
PushMessage(AppletMessage::OperationModeChanged);
PushMessage(AppletMessage::PerformanceModeChanged);
- on_operation_mode_changed.writable->Signal();
+ on_operation_mode_changed->GetWritableEvent()->Signal();
}
void AppletMessageQueue::RequestExit() {
@@ -635,7 +638,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
- {53, nullptr, "BeginVrModeEx"},
+ {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
@@ -732,6 +735,13 @@ void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx
rb.Push(RESULT_SUCCESS);
}
+void ICommonStateGetter::BeginVrModeEx(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -856,7 +866,7 @@ public:
{25, nullptr, "Terminate"},
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
- {60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"},
+ {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"},
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
{102, nullptr, "PushExtraStorage"},
@@ -900,6 +910,13 @@ private:
rb.Push(applet->GetStatus());
}
+ void PresetLibraryAppletGpuTimeSliceZero(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
void Start(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
@@ -1030,20 +1047,21 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
const u64 offset{rp.Pop<u64>()};
const std::vector<u8> data{ctx.ReadBuffer()};
+ const std::size_t size{std::min(data.size(), backing.GetSize() - offset)};
- LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
+ LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
- if (data.size() > backing.GetSize() - offset) {
+ if (offset > backing.GetSize()) {
LOG_ERROR(Service_AM,
"offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
- backing.GetSize(), data.size(), offset);
+ backing.GetSize(), size, offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
return;
}
- std::memcpy(backing.GetData().data() + offset, data.data(), data.size());
+ std::memcpy(backing.GetData().data() + offset, data.data(), size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -1053,11 +1071,11 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
- const std::size_t size{ctx.GetWriteBufferSize()};
+ const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)};
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
- if (size > backing.GetSize() - offset) {
+ if (offset > backing.GetSize()) {
LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
backing.GetSize(), size, offset);
@@ -1178,7 +1196,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
- {65, nullptr, "IsGamePlayRecordingSupported"},
+ {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"},
{66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
{67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
{68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
@@ -1199,10 +1217,10 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
{140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
- {141, nullptr, "TryPopFromFriendInvitationStorageChannel"},
+ {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
{150, nullptr, "GetNotificationStorageChannelEvent"},
{151, nullptr, "TryPopFromNotificationStorageChannel"},
- {160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
+ {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
{170, nullptr, "SetHdcpAuthenticationActivated"},
{180, nullptr, "GetLaunchRequiredVersion"},
{181, nullptr, "UpgradeLaunchRequiredVersion"},
@@ -1215,11 +1233,15 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
RegisterHandlers(functions);
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");
+ gpu_error_detected_event =
+ Kernel::KEvent::Create(kernel, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
+ gpu_error_detected_event->Initialize();
+ friend_invitation_storage_channel_event =
+ Kernel::KEvent::Create(kernel, "IApplicationFunctions:FriendInvitationStorageChannelEvent");
+ friend_invitation_storage_channel_event->Initialize();
+ health_warning_disappeared_system_event =
+ Kernel::KEvent::Create(kernel, "IApplicationFunctions:HealthWarningDisappearedSystemEvent");
+ health_warning_disappeared_system_event->Initialize();
}
IApplicationFunctions::~IApplicationFunctions() = default;
@@ -1466,6 +1488,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
rb.Push(*res_code);
}
+void IApplicationFunctions::IsGamePlayRecordingSupported(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ constexpr bool gameplay_recording_supported = false;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(gameplay_recording_supported);
+}
+
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -1606,7 +1638,7 @@ void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestCon
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(gpu_error_detected_event.readable);
+ rb.PushCopyObjects(gpu_error_detected_event->GetReadableEvent());
}
void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx) {
@@ -1614,7 +1646,23 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(friend_invitation_storage_channel_event.readable);
+ rb.PushCopyObjects(friend_invitation_storage_channel_event->GetReadableEvent());
+}
+
+void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(
+ Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NO_DATA_IN_CHANNEL);
+}
+
+void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
}
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
@@ -1650,8 +1698,9 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
RegisterHandlers(functions);
- pop_from_general_channel_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), "IHomeMenuFunctions:PopFromGeneralChannelEvent");
+ pop_from_general_channel_event =
+ Kernel::KEvent::Create(system.Kernel(), "IHomeMenuFunctions:PopFromGeneralChannelEvent");
+ pop_from_general_channel_event->Initialize();
}
IHomeMenuFunctions::~IHomeMenuFunctions() = default;
@@ -1668,7 +1717,7 @@ void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(Kernel::HLERequestContext
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(pop_from_general_channel_event.readable);
+ rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent());
}
IGlobalStateController::IGlobalStateController(Core::System& system_)
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index f51aca1af..6911f0d6e 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -7,11 +7,12 @@
#include <chrono>
#include <memory>
#include <queue>
-#include "core/hle/kernel/writable_event.h"
+
#include "core/hle/service/service.h"
namespace Kernel {
class KernelCore;
+class KEvent;
class TransferMemory;
} // namespace Kernel
@@ -55,8 +56,8 @@ public:
explicit AppletMessageQueue(Kernel::KernelCore& kernel);
~AppletMessageQueue();
- const std::shared_ptr<Kernel::ReadableEvent>& GetMessageReceiveEvent() const;
- const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const;
+ const std::shared_ptr<Kernel::KReadableEvent>& GetMessageReceiveEvent() const;
+ const std::shared_ptr<Kernel::KReadableEvent>& GetOperationModeChangedEvent() const;
void PushMessage(AppletMessage msg);
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
@@ -65,8 +66,8 @@ public:
private:
std::queue<AppletMessage> messages;
- Kernel::EventPair on_new_message;
- Kernel::EventPair on_operation_mode_changed;
+ std::shared_ptr<Kernel::KEvent> on_new_message;
+ std::shared_ptr<Kernel::KEvent> on_operation_mode_changed;
};
class IWindowController final : public ServiceFramework<IWindowController> {
@@ -153,8 +154,8 @@ private:
};
NVFlinger::NVFlinger& nvflinger;
- Kernel::EventPair launchable_event;
- Kernel::EventPair accumulated_suspended_tick_changed_event;
+ std::shared_ptr<Kernel::KEvent> launchable_event;
+ std::shared_ptr<Kernel::KEvent> accumulated_suspended_tick_changed_event;
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
@@ -189,6 +190,7 @@ private:
void IsVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
+ void BeginVrModeEx(Kernel::HLERequestContext& ctx);
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
@@ -265,6 +267,7 @@ private:
void SetTerminateResult(Kernel::HLERequestContext& ctx);
void GetDisplayVersion(Kernel::HLERequestContext& ctx);
void GetDesiredLanguage(Kernel::HLERequestContext& ctx);
+ void IsGamePlayRecordingSupported(Kernel::HLERequestContext& ctx);
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx);
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
@@ -287,12 +290,15 @@ private:
void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
+ void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
+ void GetHealthWarningDisappearedSystemEvent(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;
+ std::shared_ptr<Kernel::KEvent> gpu_error_detected_event;
+ std::shared_ptr<Kernel::KEvent> friend_invitation_storage_channel_event;
+ std::shared_ptr<Kernel::KEvent> health_warning_disappeared_system_event;
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
@@ -304,7 +310,7 @@ private:
void RequestToGetForeground(Kernel::HLERequestContext& ctx);
void GetPopFromGeneralChannelEvent(Kernel::HLERequestContext& ctx);
- Kernel::EventPair pop_from_general_channel_event;
+ std::shared_ptr<Kernel::KEvent> pop_from_general_channel_event;
};
class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 08676c3fc..e2f3b7563 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cstring>
+
#include "common/assert.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
@@ -11,9 +12,10 @@
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
-#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/server_session.h"
-#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"
@@ -27,11 +29,13 @@ namespace Service::AM::Applets {
AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) {
state_changed_event =
- Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:StateChangedEvent");
- pop_out_data_event =
- Kernel::WritableEvent::CreateEventPair(kernel, "ILibraryAppletAccessor:PopDataOutEvent");
- pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
- kernel, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
+ Kernel::KEvent::Create(kernel, "ILibraryAppletAccessor:StateChangedEvent");
+ state_changed_event->Initialize();
+ pop_out_data_event = Kernel::KEvent::Create(kernel, "ILibraryAppletAccessor:PopDataOutEvent");
+ pop_out_data_event->Initialize();
+ pop_interactive_out_data_event =
+ Kernel::KEvent::Create(kernel, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
+ pop_interactive_out_data_event->Initialize();
}
AppletDataBroker::~AppletDataBroker() = default;
@@ -58,7 +62,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
auto out = std::move(out_channel.front());
out_channel.pop_front();
- pop_out_data_event.writable->Clear();
+ pop_out_data_event->GetWritableEvent()->Clear();
return out;
}
@@ -77,7 +81,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
auto out = std::move(out_interactive_channel.front());
out_interactive_channel.pop_front();
- pop_interactive_out_data_event.writable->Clear();
+ pop_interactive_out_data_event->GetWritableEvent()->Clear();
return out;
}
@@ -96,7 +100,7 @@ void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storag
void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
out_channel.emplace_back(std::move(storage));
- pop_out_data_event.writable->Signal();
+ pop_out_data_event->GetWritableEvent()->Signal();
}
void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
@@ -105,23 +109,23 @@ void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& s
void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
out_interactive_channel.emplace_back(std::move(storage));
- pop_interactive_out_data_event.writable->Signal();
+ pop_interactive_out_data_event->GetWritableEvent()->Signal();
}
void AppletDataBroker::SignalStateChanged() const {
- state_changed_event.writable->Signal();
+ state_changed_event->GetWritableEvent()->Signal();
}
-std::shared_ptr<Kernel::ReadableEvent> AppletDataBroker::GetNormalDataEvent() const {
- return pop_out_data_event.readable;
+std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetNormalDataEvent() const {
+ return pop_out_data_event->GetReadableEvent();
}
-std::shared_ptr<Kernel::ReadableEvent> AppletDataBroker::GetInteractiveDataEvent() const {
- return pop_interactive_out_data_event.readable;
+std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetInteractiveDataEvent() const {
+ return pop_interactive_out_data_event->GetReadableEvent();
}
-std::shared_ptr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent() const {
- return state_changed_event.readable;
+std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetStateChangedEvent() const {
+ return state_changed_event->GetReadableEvent();
}
Applet::Applet(Kernel::KernelCore& kernel_) : broker{kernel_} {}
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 4fd792c05..b9a006317 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -6,9 +6,9 @@
#include <memory>
#include <queue>
+
#include "common/swap.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/writable_event.h"
union ResultCode;
@@ -29,7 +29,9 @@ class WebBrowserApplet;
namespace Kernel {
class KernelCore;
-}
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
namespace Service::AM {
@@ -87,9 +89,9 @@ public:
void SignalStateChanged() const;
- std::shared_ptr<Kernel::ReadableEvent> GetNormalDataEvent() const;
- std::shared_ptr<Kernel::ReadableEvent> GetInteractiveDataEvent() const;
- std::shared_ptr<Kernel::ReadableEvent> GetStateChangedEvent() const;
+ std::shared_ptr<Kernel::KReadableEvent> GetNormalDataEvent() const;
+ std::shared_ptr<Kernel::KReadableEvent> GetInteractiveDataEvent() const;
+ std::shared_ptr<Kernel::KReadableEvent> GetStateChangedEvent() const;
private:
// Queues are named from applet's perspective
@@ -106,13 +108,13 @@ private:
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
std::deque<std::shared_ptr<IStorage>> out_interactive_channel;
- Kernel::EventPair state_changed_event;
+ std::shared_ptr<Kernel::KEvent> state_changed_event;
// Signaled on PushNormalDataFromApplet
- Kernel::EventPair pop_out_data_event;
+ std::shared_ptr<Kernel::KEvent> pop_out_data_event;
// Signaled on PushInteractiveDataFromApplet
- Kernel::EventPair pop_interactive_out_data_event;
+ std::shared_ptr<Kernel::KEvent> pop_interactive_out_data_event;
};
class Applet {
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
index 7edfca64e..c2bfe698f 100644
--- a/src/core/hle/service/am/applets/controller.cpp
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -37,7 +37,7 @@ static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
.border_colors = std::move(identification_colors),
.enable_explain_text = enable_text,
.explain_text = std::move(text),
- .allow_pro_controller = npad_style_set.pro_controller == 1,
+ .allow_pro_controller = npad_style_set.fullkey == 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,
@@ -211,7 +211,8 @@ void Controller::Execute() {
case ControllerSupportMode::ShowControllerFirmwareUpdate:
UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
controller_private_arg.mode);
- [[fallthrough]];
+ ConfigurationComplete();
+ break;
default: {
ConfigurationComplete();
break;
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index d85505082..0c8b632e8 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -20,9 +20,9 @@ namespace Service::AM::Applets {
struct ShowError {
u8 mode;
bool jump;
- INSERT_UNION_PADDING_BYTES(4);
+ INSERT_PADDING_BYTES_NOINIT(4);
bool use_64bit_error_code;
- INSERT_UNION_PADDING_BYTES(1);
+ INSERT_PADDING_BYTES_NOINIT(1);
u64 error_code_64;
u32 error_code_32;
};
@@ -32,7 +32,7 @@ static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size.");
struct ShowErrorRecord {
u8 mode;
bool jump;
- INSERT_UNION_PADDING_BYTES(6);
+ INSERT_PADDING_BYTES_NOINIT(6);
u64 error_code_64;
u64 posix_time;
};
@@ -41,7 +41,7 @@ static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect si
struct SystemErrorArg {
u8 mode;
bool jump;
- INSERT_UNION_PADDING_BYTES(6);
+ INSERT_PADDING_BYTES_NOINIT(6);
u64 error_code_64;
std::array<char, 8> language_code;
std::array<char, 0x800> main_text;
@@ -52,7 +52,7 @@ static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect si
struct ApplicationErrorArg {
u8 mode;
bool jump;
- INSERT_UNION_PADDING_BYTES(6);
+ INSERT_PADDING_BYTES_NOINIT(6);
u32 error_code;
std::array<char, 8> language_code;
std::array<char, 0x800> main_text;
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 3022438b1..79b209c6b 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -121,6 +121,10 @@ void SoftwareKeyboard::ExecuteInteractive() {
std::memcpy(&request, data.data(), sizeof(Request));
switch (request) {
+ case Request::Finalize:
+ complete = true;
+ broker.SignalStateChanged();
+ break;
case Request::Calc: {
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{1}));
broker.SignalStateChanged();
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 23e28565b..8d657c0bf 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -5,6 +5,7 @@
#include <algorithm>
#include <numeric>
#include <vector>
+
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/common_funcs.h"
@@ -14,10 +15,10 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/loader/loader.h"
#include "core/settings.h"
@@ -62,8 +63,9 @@ public:
RegisterHandlers(functions);
- purchased_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), "IPurchaseEventManager:PurchasedEvent");
+ purchased_event =
+ Kernel::KEvent::Create(system.Kernel(), "IPurchaseEventManager:PurchasedEvent");
+ purchased_event->Initialize();
}
private:
@@ -96,10 +98,10 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(purchased_event.readable);
+ rb.PushCopyObjects(purchased_event->GetReadableEvent());
}
- Kernel::EventPair purchased_event;
+ std::shared_ptr<Kernel::KEvent> purchased_event;
};
AOC_U::AOC_U(Core::System& system_)
@@ -124,8 +126,8 @@ AOC_U::AOC_U(Core::System& system_)
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- aoc_change_event =
- Kernel::WritableEvent::CreateEventPair(kernel, "GetAddOnContentListChanged:Event");
+ aoc_change_event = Kernel::KEvent::Create(kernel, "GetAddOnContentListChanged:Event");
+ aoc_change_event->Initialize();
}
AOC_U::~AOC_U() = default;
@@ -252,7 +254,7 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(aoc_change_event.readable);
+ rb.PushCopyObjects(aoc_change_event->GetReadableEvent());
}
void AOC_U::CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 26ee51be0..1aa23529e 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -11,7 +11,7 @@ class System;
}
namespace Kernel {
-class WritableEvent;
+class KEvent;
}
namespace Service::AOC {
@@ -31,7 +31,7 @@ private:
void CreatePermanentEcPurchasedEventManager(Kernel::HLERequestContext& ctx);
std::vector<u64> add_on_content;
- Kernel::EventPair aoc_change_event;
+ std::shared_ptr<Kernel::KEvent> aoc_change_event;
};
/// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index 298f6d520..0bff97a37 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -56,7 +56,7 @@ APM::APM(Core::System& system_, std::shared_ptr<Module> apm_, Controller& contro
static const FunctionInfo functions[] = {
{0, &APM::OpenSession, "OpenSession"},
{1, &APM::GetPerformanceMode, "GetPerformanceMode"},
- {6, nullptr, "IsCpuOverclockEnabled"},
+ {6, &APM::IsCpuOverclockEnabled, "IsCpuOverclockEnabled"},
};
RegisterHandlers(functions);
}
@@ -78,6 +78,14 @@ void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
rb.PushEnum(controller.GetCurrentPerformanceMode());
}
+void APM::IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_APM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(false);
+}
+
APM_Sys::APM_Sys(Core::System& system_, Controller& controller_)
: ServiceFramework{system_, "apm:sys"}, controller{controller_} {
// clang-format off
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h
index 7d57c4978..063ad5308 100644
--- a/src/core/hle/service/apm/interface.h
+++ b/src/core/hle/service/apm/interface.h
@@ -20,6 +20,7 @@ public:
private:
void OpenSession(Kernel::HLERequestContext& ctx);
void GetPerformanceMode(Kernel::HLERequestContext& ctx);
+ void IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx);
std::shared_ptr<Module> apm;
Controller& controller;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 0cd797109..5ed9cb20e 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -14,9 +14,10 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/errors.h"
#include "core/memory.h"
@@ -29,7 +30,7 @@ constexpr int DefaultSampleRate{48000};
struct AudoutParams {
s32_le sample_rate;
u16_le channel_count;
- INSERT_PADDING_BYTES(2);
+ INSERT_PADDING_BYTES_NOINIT(2);
};
static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
@@ -58,7 +59,7 @@ public:
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
- {11, nullptr, "FlushAudioOutBuffers"},
+ {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
{12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
{13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
};
@@ -66,13 +67,13 @@ public:
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
- buffer_event =
- Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
+ buffer_event = Kernel::KEvent::Create(system.Kernel(), "IAudioOutBufferReleased");
+ buffer_event->Initialize();
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
audio_params.channel_count, std::move(unique_name), [this] {
const auto guard = LockService();
- buffer_event.writable->Signal();
+ buffer_event->GetWritableEvent()->Signal();
});
}
@@ -125,7 +126,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(buffer_event.readable);
+ rb.PushCopyObjects(buffer_event->GetReadableEvent());
}
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
@@ -185,6 +186,14 @@ private:
rb.Push(static_cast<u32>(stream->GetQueueSize()));
}
+ void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(stream->Flush());
+ }
+
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const float volume = rp.Pop<float>();
@@ -211,7 +220,7 @@ private:
[[maybe_unused]] AudoutParams audio_params{};
/// This is the event handle used to check if the audio buffer was released
- Kernel::EventPair buffer_event;
+ std::shared_ptr<Kernel::KEvent> buffer_event;
Core::Memory::Memory& main_memory;
};
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index c5c22d053..b2b2ffc5a 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -16,9 +16,10 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/errors.h"
@@ -47,13 +48,13 @@ public:
// clang-format on
RegisterHandlers(functions);
- system_event =
- Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
+ system_event = Kernel::KEvent::Create(system.Kernel(), "IAudioRenderer:SystemEvent");
+ system_event->Initialize();
renderer = std::make_unique<AudioCore::AudioRenderer>(
system.CoreTiming(), system.Memory(), audren_params,
[this]() {
const auto guard = LockService();
- system_event.writable->Signal();
+ system_event->GetWritableEvent()->Signal();
},
instance_number);
}
@@ -126,7 +127,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(system_event.readable);
+ rb.PushCopyObjects(system_event->GetReadableEvent());
}
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
@@ -160,7 +161,7 @@ private:
rb.Push(ERR_NOT_SUPPORTED);
}
- Kernel::EventPair system_event;
+ std::shared_ptr<Kernel::KEvent> system_event;
std::unique_ptr<AudioCore::AudioRenderer> renderer;
u32 rendering_time_limit_percent = 100;
};
@@ -187,17 +188,19 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- buffer_event =
- Kernel::WritableEvent::CreateEventPair(kernel, "IAudioOutBufferReleasedEvent");
+ buffer_event = Kernel::KEvent::Create(kernel, "IAudioOutBufferReleasedEvent");
+ buffer_event->Initialize();
// Should be similar to audio_output_device_switch_event
- audio_input_device_switch_event = Kernel::WritableEvent::CreateEventPair(
- kernel, "IAudioDevice:AudioInputDeviceSwitchedEvent");
+ audio_input_device_switch_event =
+ Kernel::KEvent::Create(kernel, "IAudioDevice:AudioInputDeviceSwitchedEvent");
+ audio_input_device_switch_event->Initialize();
// Should only be signalled when an audio output device has been changed, example: speaker
// to headset
- audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair(
- kernel, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
+ audio_output_device_switch_event =
+ Kernel::KEvent::Create(kernel, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
+ audio_output_device_switch_event->Initialize();
}
private:
@@ -286,11 +289,11 @@ private:
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
- buffer_event.writable->Signal();
+ buffer_event->GetWritableEvent()->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(buffer_event.readable);
+ rb.PushCopyObjects(buffer_event->GetReadableEvent());
}
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
@@ -307,7 +310,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(audio_input_device_switch_event.readable);
+ rb.PushCopyObjects(audio_input_device_switch_event->GetReadableEvent());
}
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
@@ -315,13 +318,13 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(audio_output_device_switch_event.readable);
+ rb.PushCopyObjects(audio_output_device_switch_event->GetReadableEvent());
}
u32_le revision = 0;
- Kernel::EventPair buffer_event;
- Kernel::EventPair audio_input_device_switch_event;
- Kernel::EventPair audio_output_device_switch_event;
+ std::shared_ptr<Kernel::KEvent> buffer_event;
+ std::shared_ptr<Kernel::KEvent> audio_input_device_switch_event;
+ std::shared_ptr<Kernel::KEvent> audio_output_device_switch_event;
}; // namespace Audio
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index 174388445..92d25dbe4 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -5,6 +5,9 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/service/bcat/backend/backend.h"
@@ -12,12 +15,13 @@ namespace Service::BCAT {
ProgressServiceBackend::ProgressServiceBackend(Kernel::KernelCore& kernel,
std::string_view event_name) {
- event = Kernel::WritableEvent::CreateEventPair(
- kernel, std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
+ event = Kernel::KEvent::Create(kernel,
+ "ProgressServiceBackend:UpdateEvent:" + std::string(event_name));
+ event->Initialize();
}
-std::shared_ptr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const {
- return event.readable;
+std::shared_ptr<Kernel::KReadableEvent> ProgressServiceBackend::GetEvent() const {
+ return event->GetReadableEvent();
}
DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
@@ -85,9 +89,9 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
void ProgressServiceBackend::SignalUpdate() const {
if (need_hle_lock) {
std::lock_guard lock(HLE::g_hle_lock);
- event.writable->Signal();
+ event->GetWritableEvent()->Signal();
} else {
- event.writable->Signal();
+ event->GetWritableEvent()->Signal();
}
}
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index 48bbbe66f..db585b069 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -11,8 +11,6 @@
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/result.h"
namespace Core {
@@ -21,7 +19,9 @@ class System;
namespace Kernel {
class KernelCore;
-}
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
namespace Service::BCAT {
@@ -98,13 +98,13 @@ public:
private:
explicit ProgressServiceBackend(Kernel::KernelCore& kernel, std::string_view event_name);
- std::shared_ptr<Kernel::ReadableEvent> GetEvent() const;
+ std::shared_ptr<Kernel::KReadableEvent> GetEvent() const;
DeliveryCacheProgressImpl& GetImpl();
void SignalUpdate() const;
DeliveryCacheProgressImpl impl{};
- Kernel::EventPair event;
+ std::shared_ptr<Kernel::KEvent> event;
bool need_hle_lock = false;
};
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index b8696a395..503109fdd 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -11,9 +11,9 @@
#include "core/core.h"
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bcat/module.h"
@@ -89,7 +89,7 @@ struct DeliveryCacheDirectoryEntry {
class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
public:
explicit IDeliveryCacheProgressService(Core::System& system_,
- std::shared_ptr<Kernel::ReadableEvent> event_,
+ std::shared_ptr<Kernel::KReadableEvent> event_,
const DeliveryCacheProgressImpl& impl_)
: ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{std::move(event_)},
impl{impl_} {
@@ -121,7 +121,7 @@ private:
rb.Push(RESULT_SUCCESS);
}
- std::shared_ptr<Kernel::ReadableEvent> event;
+ std::shared_ptr<Kernel::KReadableEvent> event;
const DeliveryCacheProgressImpl& impl;
};
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 2de86f1f1..17a2ac899 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -6,9 +6,9 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -35,7 +35,8 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- register_event = Kernel::WritableEvent::CreateEventPair(kernel, "BT:RegisterEvent");
+ register_event = Kernel::KEvent::Create(kernel, "BT:RegisterEvent");
+ register_event->Initialize();
}
private:
@@ -44,10 +45,10 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(register_event.readable);
+ rb.PushCopyObjects(register_event->GetReadableEvent());
}
- Kernel::EventPair register_event;
+ std::shared_ptr<Kernel::KEvent> register_event;
};
class BtDrv final : public ServiceFramework<BtDrv> {
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 38b55300e..9cf2ee92a 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -8,9 +8,9 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/btm/btm.h"
#include "core/hle/service/service.h"
@@ -58,12 +58,14 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- scan_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ScanEvent");
- connection_event =
- Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConnectionEvent");
- service_discovery =
- Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:Discovery");
- config_event = Kernel::WritableEvent::CreateEventPair(kernel, "IBtmUserCore:ConfigEvent");
+ scan_event = Kernel::KEvent::Create(kernel, "IBtmUserCore:ScanEvent");
+ scan_event->Initialize();
+ connection_event = Kernel::KEvent::Create(kernel, "IBtmUserCore:ConnectionEvent");
+ connection_event->Initialize();
+ service_discovery = Kernel::KEvent::Create(kernel, "IBtmUserCore:Discovery");
+ service_discovery->Initialize();
+ config_event = Kernel::KEvent::Create(kernel, "IBtmUserCore:ConfigEvent");
+ config_event->Initialize();
}
private:
@@ -72,7 +74,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(scan_event.readable);
+ rb.PushCopyObjects(scan_event->GetReadableEvent());
}
void AcquireBleConnectionEvent(Kernel::HLERequestContext& ctx) {
@@ -80,7 +82,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(connection_event.readable);
+ rb.PushCopyObjects(connection_event->GetReadableEvent());
}
void AcquireBleServiceDiscoveryEvent(Kernel::HLERequestContext& ctx) {
@@ -88,7 +90,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(service_discovery.readable);
+ rb.PushCopyObjects(service_discovery->GetReadableEvent());
}
void AcquireBleMtuConfigEvent(Kernel::HLERequestContext& ctx) {
@@ -96,13 +98,13 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(config_event.readable);
+ rb.PushCopyObjects(config_event->GetReadableEvent());
}
- Kernel::EventPair scan_event;
- Kernel::EventPair connection_event;
- Kernel::EventPair service_discovery;
- Kernel::EventPair config_event;
+ std::shared_ptr<Kernel::KEvent> scan_event;
+ std::shared_ptr<Kernel::KEvent> connection_event;
+ std::shared_ptr<Kernel::KEvent> service_discovery;
+ std::shared_ptr<Kernel::KEvent> config_event;
};
class BTM_USR final : public ServiceFramework<BTM_USR> {
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index c5b053c31..72a877d68 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -7,8 +7,9 @@
#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"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/friend/errors.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/friend/interface.h"
@@ -183,8 +184,9 @@ public:
RegisterHandlers(functions);
- notification_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), "INotificationService:NotifyEvent");
+ notification_event =
+ Kernel::KEvent::Create(system.Kernel(), "INotificationService:NotifyEvent");
+ notification_event->Initialize();
}
private:
@@ -193,7 +195,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(notification_event.readable);
+ rb.PushCopyObjects(notification_event->GetReadableEvent());
}
void Clear(Kernel::HLERequestContext& ctx) {
@@ -258,7 +260,7 @@ private:
};
Common::UUID uuid{Common::INVALID_UUID};
- Kernel::EventPair notification_event;
+ std::shared_ptr<Kernel::KEvent> notification_event;
std::queue<SizedNotificationInfo> notifications;
States states{};
};
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 59b694cd4..c4a59147d 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -39,16 +39,25 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing,
cur_entry.sampling_number2 = cur_entry.sampling_number;
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);
- }
+ using namespace Settings::NativeKeyboard;
+
+ // TODO: Assign the correct key to all modifiers
+ cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus());
+ cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus());
+ cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus());
+ cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus());
+ cur_entry.modifier.gui.Assign(0);
+ cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus());
+ cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus());
+ cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus());
+ cur_entry.modifier.katakana.Assign(0);
+ cur_entry.modifier.hiragana.Assign(0);
}
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index f3eef5936..b5b281752 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -31,12 +32,28 @@ public:
void OnLoadInputDevices() override;
private:
+ struct Modifiers {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32> control;
+ BitField<1, 1, u32> shift;
+ BitField<2, 1, u32> left_alt;
+ BitField<3, 1, u32> right_alt;
+ BitField<4, 1, u32> gui;
+ BitField<8, 1, u32> caps_lock;
+ BitField<9, 1, u32> scroll_lock;
+ BitField<10, 1, u32> num_lock;
+ BitField<11, 1, u32> katakana;
+ BitField<12, 1, u32> hiragana;
+ };
+ };
+ static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size");
+
struct KeyboardState {
s64_le sampling_number;
s64_le sampling_number2;
- s32_le modifier;
- s32_le attribute;
+ Modifiers modifier;
std::array<u8, 32> key;
};
static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index ac40989c5..2e7457604 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -36,6 +36,7 @@ void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
+ cur_entry.attribute.raw = 0;
if (Settings::values.mouse_enabled) {
const auto [px, py, sx, sy] = mouse_device->GetStatus();
const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
@@ -46,10 +47,14 @@ void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
cur_entry.delta_y = y - last_entry.y;
cur_entry.mouse_wheel_x = sx;
cur_entry.mouse_wheel_y = sy;
+ cur_entry.attribute.is_connected.Assign(1);
- for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
- cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
- }
+ using namespace Settings::NativeMouseButton;
+ cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus());
+ cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus());
+ cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus());
+ cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus());
+ cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus());
}
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 357ab7107..3b432a36e 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/frontend/input.h"
@@ -30,6 +31,27 @@ public:
void OnLoadInputDevices() override;
private:
+ struct Buttons {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32> left;
+ BitField<1, 1, u32> right;
+ BitField<2, 1, u32> middle;
+ BitField<3, 1, u32> forward;
+ BitField<4, 1, u32> back;
+ };
+ };
+ static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size");
+
+ struct Attributes {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32> transferable;
+ BitField<1, 1, u32> is_connected;
+ };
+ };
+ static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
+
struct MouseState {
s64_le sampling_number;
s64_le sampling_number2;
@@ -39,8 +61,8 @@ private:
s32_le delta_y;
s32_le mouse_wheel_x;
s32_le mouse_wheel_y;
- s32_le button;
- s32_le attribute;
+ Buttons button;
+ Attributes attribute;
};
static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index d280e7caf..70b9f3824 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -12,14 +12,16 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/input.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/settings.h"
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
+constexpr s32 HID_TRIGGER_MAX = 0x7fff;
[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
@@ -47,6 +49,8 @@ Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
return NPadControllerType::JoyRight;
case Settings::ControllerType::Handheld:
return NPadControllerType::Handheld;
+ case Settings::ControllerType::GameCube:
+ return NPadControllerType::GameCube;
default:
UNREACHABLE();
return NPadControllerType::ProController;
@@ -66,6 +70,8 @@ Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
return Settings::ControllerType::RightJoycon;
case NPadControllerType::Handheld:
return Settings::ControllerType::Handheld;
+ case NPadControllerType::GameCube:
+ return Settings::ControllerType::GameCube;
default:
UNREACHABLE();
return Settings::ControllerType::ProController;
@@ -141,7 +147,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) {
device_handle.device_index < DeviceIndex::MaxDeviceIndex;
}
-Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
+Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {
+ latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE});
+}
Controller_NPad::~Controller_NPad() {
OnRelease();
@@ -151,79 +159,93 @@ 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();
+ styleset_changed_events[controller_idx]->GetWritableEvent()->Signal();
return;
}
- controller.joy_styles.raw = 0; // Zero out
+ controller.style_set.raw = 0; // Zero out
controller.device_type.raw = 0;
- controller.properties.raw = 0;
+ controller.system_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;
+ controller.style_set.fullkey.Assign(1);
+ controller.device_type.fullkey.Assign(1);
+ controller.system_properties.is_vertical.Assign(1);
+ controller.system_properties.use_plus.Assign(1);
+ controller.system_properties.use_minus.Assign(1);
+ controller.assignment_mode = NpadAssignments::Single;
+ controller.footer_type = AppletFooterUiType::SwitchProController;
break;
case NPadControllerType::Handheld:
- controller.joy_styles.handheld.Assign(1);
- controller.device_type.handheld.Assign(1);
- controller.properties.is_vertical.Assign(1);
- controller.properties.use_plus.Assign(1);
- controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NpadAssignments::Dual;
+ controller.style_set.handheld.Assign(1);
+ controller.device_type.handheld_left.Assign(1);
+ controller.device_type.handheld_right.Assign(1);
+ controller.system_properties.is_vertical.Assign(1);
+ controller.system_properties.use_plus.Assign(1);
+ controller.system_properties.use_minus.Assign(1);
+ controller.assignment_mode = NpadAssignments::Dual;
+ controller.footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
break;
case NPadControllerType::JoyDual:
- controller.joy_styles.joycon_dual.Assign(1);
+ controller.style_set.joycon_dual.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.device_type.joycon_right.Assign(1);
- controller.properties.is_vertical.Assign(1);
- controller.properties.use_plus.Assign(1);
- controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NpadAssignments::Dual;
+ controller.system_properties.is_vertical.Assign(1);
+ controller.system_properties.use_plus.Assign(1);
+ controller.system_properties.use_minus.Assign(1);
+ controller.assignment_mode = NpadAssignments::Dual;
+ controller.footer_type = AppletFooterUiType::JoyDual;
break;
case NPadControllerType::JoyLeft:
- controller.joy_styles.joycon_left.Assign(1);
+ controller.style_set.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.system_properties.is_horizontal.Assign(1);
+ controller.system_properties.use_minus.Assign(1);
+ controller.assignment_mode = NpadAssignments::Single;
+ controller.footer_type = AppletFooterUiType::JoyLeftHorizontal;
break;
case NPadControllerType::JoyRight:
- controller.joy_styles.joycon_right.Assign(1);
+ controller.style_set.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.system_properties.is_horizontal.Assign(1);
+ controller.system_properties.use_plus.Assign(1);
+ controller.assignment_mode = NpadAssignments::Single;
+ controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
+ break;
+ case NPadControllerType::GameCube:
+ controller.style_set.gamecube.Assign(1);
+ // The GC Controller behaves like a wired Pro Controller
+ controller.device_type.fullkey.Assign(1);
+ controller.system_properties.is_vertical.Assign(1);
+ controller.system_properties.use_plus.Assign(1);
break;
case NPadControllerType::Pokeball:
- controller.joy_styles.pokeball.Assign(1);
- controller.device_type.pokeball.Assign(1);
- controller.pad_assignment = NpadAssignments::Single;
+ controller.style_set.palma.Assign(1);
+ controller.device_type.palma.Assign(1);
+ controller.assignment_mode = NpadAssignments::Single;
break;
}
- controller.single_color_error = ColorReadError::ReadOk;
- controller.single_color.body_color = 0;
- controller.single_color.button_color = 0;
+ controller.fullkey_color.attribute = ColorAttributes::Ok;
+ controller.fullkey_color.fullkey.body = 0;
+ controller.fullkey_color.fullkey.button = 0;
- controller.dual_color_error = ColorReadError::ReadOk;
- controller.left_color.body_color =
+ controller.joycon_color.attribute = ColorAttributes::Ok;
+ controller.joycon_color.left.body =
Settings::values.players.GetValue()[controller_idx].body_color_left;
- controller.left_color.button_color =
+ controller.joycon_color.left.button =
Settings::values.players.GetValue()[controller_idx].button_color_left;
- controller.right_color.body_color =
+ controller.joycon_color.right.body =
Settings::values.players.GetValue()[controller_idx].body_color_right;
- controller.right_color.button_color =
+ controller.joycon_color.right.button =
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;
+ // TODO: Investigate when we should report all batery types
+ controller.battery_level_dual = BATTERY_FULL;
+ controller.battery_level_left = BATTERY_FULL;
+ controller.battery_level_right = BATTERY_FULL;
SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
}
@@ -231,8 +253,9 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
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));
+ styleset_changed_events[i] =
+ Kernel::KEvent::Create(kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
+ styleset_changed_events[i]->Initialize();
}
if (!IsControllerActivated()) {
@@ -247,8 +270,9 @@ void Controller_NPad::OnInit() {
style.joycon_left.Assign(1);
style.joycon_right.Assign(1);
style.joycon_dual.Assign(1);
- style.pro_controller.Assign(1);
- style.pokeball.Assign(1);
+ style.fullkey.Assign(1);
+ style.gamecube.Assign(1);
+ style.palma.Assign(1);
}
std::transform(Settings::values.players.GetValue().begin(),
@@ -328,6 +352,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
auto& pad_state = npad_pad_states[controller_idx].pad_states;
auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
+ auto& trigger_entry = npad_trigger_states[controller_idx];
const auto& button_state = buttons[controller_idx];
const auto& analog_state = sticks[controller_idx];
const auto [stick_l_x_f, stick_l_y_f] =
@@ -393,6 +418,17 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
}
+
+ if (controller_type == NPadControllerType::GameCube) {
+ trigger_entry.l_analog = static_cast<s32>(
+ button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
+ trigger_entry.r_analog = static_cast<s32>(
+ button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
+ pad_state.zl.Assign(false);
+ pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ }
}
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -402,13 +438,15 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
}
for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
auto& npad = shared_memory_entries[i];
- const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
- &npad.handheld_states,
- &npad.dual_states,
- &npad.left_joy_states,
- &npad.right_joy_states,
- &npad.pokeball_states,
- &npad.libnx};
+ const std::array<NPadGeneric*, 7> controller_npads{
+ &npad.fullkey_states, &npad.handheld_states, &npad.joy_dual_states,
+ &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states,
+ &npad.system_ext_states};
+
+ // There is the posibility to have more controllers with analog triggers
+ const std::array<TriggerGeneric*, 1> controller_triggers{
+ &npad.gc_trigger_states,
+ };
for (auto* main_controller : controller_npads) {
main_controller->common.entry_count = 16;
@@ -427,6 +465,21 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
cur_entry.timestamp2 = cur_entry.timestamp;
}
+ for (auto* analog_trigger : controller_triggers) {
+ analog_trigger->entry_count = 16;
+ analog_trigger->total_entry_count = 17;
+
+ const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
+
+ analog_trigger->timestamp = core_timing.GetCPUTicks();
+ analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
+
+ auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
+
+ cur_entry.timestamp = last_entry.timestamp + 1;
+ cur_entry.timestamp2 = cur_entry.timestamp;
+ }
+
const auto& controller_type = connected_controllers[i].type;
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
@@ -436,21 +489,24 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
RequestPadStateUpdate(npad_index);
auto& pad_state = npad_pad_states[npad_index];
+ auto& trigger_state = npad_trigger_states[npad_index];
auto& main_controller =
- npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
+ npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
auto& handheld_entry =
npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
- auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
- auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
+ auto& dual_entry = npad.joy_dual_states.npad[npad.joy_dual_states.common.last_entry_index];
+ auto& left_entry = npad.joy_left_states.npad[npad.joy_left_states.common.last_entry_index];
auto& right_entry =
- npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
- auto& pokeball_entry =
- npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
- auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
+ npad.joy_right_states.npad[npad.joy_right_states.common.last_entry_index];
+ auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
+ auto& libnx_entry =
+ npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
+ auto& trigger_entry =
+ npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
libnx_entry.connection_status.raw = 0;
- libnx_entry.connection_status.IsConnected.Assign(1);
+ libnx_entry.connection_status.is_connected.Assign(1);
switch (controller_type) {
case NPadControllerType::None:
@@ -458,67 +514,79 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
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.connection_status.is_connected.Assign(1);
+ main_controller.connection_status.is_wired.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);
+ libnx_entry.connection_status.is_wired.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);
- handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
- handheld_entry.connection_status.IsRightJoyWired.Assign(1);
+ handheld_entry.connection_status.is_connected.Assign(1);
+ handheld_entry.connection_status.is_wired.Assign(1);
+ handheld_entry.connection_status.is_left_connected.Assign(1);
+ handheld_entry.connection_status.is_right_connected.Assign(1);
+ handheld_entry.connection_status.is_left_wired.Assign(1);
+ handheld_entry.connection_status.is_right_wired.Assign(1);
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);
+ libnx_entry.connection_status.is_wired.Assign(1);
+ libnx_entry.connection_status.is_left_connected.Assign(1);
+ libnx_entry.connection_status.is_right_connected.Assign(1);
+ libnx_entry.connection_status.is_left_wired.Assign(1);
+ libnx_entry.connection_status.is_right_wired.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.is_connected.Assign(1);
+ dual_entry.connection_status.is_left_connected.Assign(1);
+ dual_entry.connection_status.is_right_connected.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);
+ libnx_entry.connection_status.is_left_connected.Assign(1);
+ libnx_entry.connection_status.is_right_connected.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.connection_status.is_connected.Assign(1);
+ left_entry.connection_status.is_left_connected.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);
+ libnx_entry.connection_status.is_left_connected.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.connection_status.is_connected.Assign(1);
+ right_entry.connection_status.is_right_connected.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);
+ libnx_entry.connection_status.is_right_connected.Assign(1);
+ break;
+ case NPadControllerType::GameCube:
+ main_controller.connection_status.raw = 0;
+ main_controller.connection_status.is_connected.Assign(1);
+ main_controller.connection_status.is_wired.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;
+ trigger_entry.l_analog = trigger_state.l_analog;
+ trigger_entry.r_analog = trigger_state.r_analog;
+
+ libnx_entry.connection_status.is_wired.Assign(1);
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
- pokeball_entry.connection_status.IsConnected.Assign(1);
+ pokeball_entry.connection_status.is_connected.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;
@@ -552,7 +620,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
const std::array<SixAxisGeneric*, 6> controller_sixaxes{
- &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
+ &npad.sixaxis_fullkey, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
&npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
};
@@ -590,7 +658,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
auto& full_sixaxis_entry =
- npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
+ npad.sixaxis_fullkey.sixaxis[npad.sixaxis_fullkey.common.last_entry_index];
auto& handheld_sixaxis_entry =
npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
auto& dual_left_sixaxis_entry =
@@ -607,7 +675,9 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
UNREACHABLE();
break;
case NPadControllerType::ProController:
+ full_sixaxis_entry.attribute.raw = 0;
if (sixaxis_sensors_enabled && motions[i][0]) {
+ full_sixaxis_entry.attribute.is_connected.Assign(1);
full_sixaxis_entry.accel = motion_devices[0].accel;
full_sixaxis_entry.gyro = motion_devices[0].gyro;
full_sixaxis_entry.rotation = motion_devices[0].rotation;
@@ -615,7 +685,9 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
break;
case NPadControllerType::Handheld:
+ handheld_sixaxis_entry.attribute.raw = 0;
if (sixaxis_sensors_enabled && motions[i][0]) {
+ handheld_sixaxis_entry.attribute.is_connected.Assign(1);
handheld_sixaxis_entry.accel = motion_devices[0].accel;
handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
@@ -623,8 +695,11 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
break;
case NPadControllerType::JoyDual:
+ dual_left_sixaxis_entry.attribute.raw = 0;
+ dual_right_sixaxis_entry.attribute.raw = 0;
if (sixaxis_sensors_enabled && motions[i][0]) {
// Set motion for the left joycon
+ dual_left_sixaxis_entry.attribute.is_connected.Assign(1);
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;
@@ -632,6 +707,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
if (sixaxis_sensors_enabled && motions[i][1]) {
// Set motion for the right joycon
+ dual_right_sixaxis_entry.attribute.is_connected.Assign(1);
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;
@@ -639,7 +715,9 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
break;
case NPadControllerType::JoyLeft:
+ left_sixaxis_entry.attribute.raw = 0;
if (sixaxis_sensors_enabled && motions[i][0]) {
+ left_sixaxis_entry.attribute.is_connected.Assign(1);
left_sixaxis_entry.accel = motion_devices[0].accel;
left_sixaxis_entry.gyro = motion_devices[0].gyro;
left_sixaxis_entry.rotation = motion_devices[0].rotation;
@@ -647,13 +725,16 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
break;
case NPadControllerType::JoyRight:
+ right_sixaxis_entry.attribute.raw = 0;
if (sixaxis_sensors_enabled && motions[i][1]) {
+ right_sixaxis_entry.attribute.is_connected.Assign(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::GameCube:
case NPadControllerType::Pokeball:
break;
}
@@ -713,8 +794,8 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
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) {
- shared_memory_entries[npad_index].pad_assignment = assignment_mode;
+ if (shared_memory_entries[npad_index].assignment_mode != assignment_mode) {
+ shared_memory_entries[npad_index].assignment_mode = assignment_mode;
}
}
@@ -732,7 +813,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size
// 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] = {};
+ latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE;
}
return false;
@@ -870,13 +951,14 @@ bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_dev
return vibration_devices_mounted[npad_index][device_index];
}
-std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
+std::shared_ptr<Kernel::KReadableEvent> Controller_NPad::GetStyleSetChangedEvent(
+ u32 npad_id) const {
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
- return styleset_event.readable;
+ return styleset_event->GetReadableEvent();
}
void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
- styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal();
+ styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent()->Signal();
}
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
@@ -890,7 +972,7 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz
return;
}
- if (controller == NPadControllerType::Handheld) {
+ if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) {
Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
MapNPadToSettingsType(controller);
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
@@ -921,9 +1003,17 @@ void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
connected_controllers[npad_index].is_connected = false;
auto& controller = shared_memory_entries[npad_index];
- controller.joy_styles.raw = 0; // Zero out
+ controller.style_set.raw = 0; // Zero out
controller.device_type.raw = 0;
- controller.properties.raw = 0;
+ controller.system_properties.raw = 0;
+ controller.button_properties.raw = 0;
+ controller.battery_level_dual = 0;
+ controller.battery_level_left = 0;
+ controller.battery_level_right = 0;
+ controller.fullkey_color = {};
+ controller.joycon_color = {};
+ controller.assignment_mode = NpadAssignments::Dual;
+ controller.footer_type = AppletFooterUiType::None;
SignalStyleSetChangedEvent(IndexToNPad(npad_index));
}
@@ -944,6 +1034,23 @@ void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
sixaxis_sensors_enabled = six_axis_status;
}
+void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) {
+ sixaxis_fusion_parameter1 = parameter1;
+ sixaxis_fusion_parameter2 = parameter2;
+}
+
+std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() {
+ return {
+ sixaxis_fusion_parameter1,
+ sixaxis_fusion_parameter2,
+ };
+}
+
+void Controller_NPad::ResetSixAxisFusionParameters() {
+ sixaxis_fusion_parameter1 = 0.0f;
+ sixaxis_fusion_parameter2 = 0.0f;
+}
+
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);
@@ -1082,15 +1189,17 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
[](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
switch (controller) {
case NPadControllerType::ProController:
- return style.pro_controller;
+ return style.fullkey;
case NPadControllerType::JoyDual:
return style.joycon_dual;
case NPadControllerType::JoyLeft:
return style.joycon_left;
case NPadControllerType::JoyRight:
return style.joycon_right;
+ case NPadControllerType::GameCube:
+ return style.gamecube;
case NPadControllerType::Pokeball:
- return style.pokeball;
+ return style.palma;
default:
return false;
}
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index e2e826623..bc2e6779d 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -10,10 +10,14 @@
#include "common/common_types.h"
#include "core/frontend/input.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
namespace Service::HID {
constexpr u32 NPAD_HANDHELD = 32;
@@ -47,6 +51,7 @@ public:
JoyDual,
JoyLeft,
JoyRight,
+ GameCube,
Pokeball,
};
@@ -56,6 +61,7 @@ public:
JoyconDual = 5,
JoyconLeft = 6,
JoyconRight = 7,
+ GameCube = 8,
Pokeball = 9,
MaxNpadType = 10,
};
@@ -90,17 +96,17 @@ public:
};
enum class NpadCommunicationMode : u64 {
- Unknown0 = 0,
- Unknown1 = 1,
- Unknown2 = 2,
- Unknown3 = 3,
+ Mode_5ms = 0,
+ Mode_10ms = 1,
+ Mode_15ms = 2,
+ Default = 3,
};
struct DeviceHandle {
- NpadType npad_type{};
- u8 npad_id{};
- DeviceIndex device_index{};
- INSERT_PADDING_BYTES(1);
+ NpadType npad_type;
+ u8 npad_id;
+ DeviceIndex device_index;
+ INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
@@ -108,25 +114,37 @@ public:
union {
u32_le raw{};
- BitField<0, 1, u32> pro_controller;
+ BitField<0, 1, u32> fullkey;
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
+ BitField<5, 1, u32> gamecube;
+ BitField<6, 1, u32> palma;
+ BitField<7, 1, u32> lark;
+ BitField<8, 1, u32> handheld_lark;
+ BitField<9, 1, u32> lucia;
+ BitField<29, 1, u32> system_ext;
+ BitField<30, 1, u32> system;
};
};
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};
+ f32 amp_low;
+ f32 freq_low;
+ f32 amp_high;
+ f32 freq_high;
};
static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
+ static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
+ .amp_low = 0.0f,
+ .freq_low = 160.0f,
+ .amp_high = 0.0f,
+ .freq_high = 320.0f,
+ };
+
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
position1.Assign(light1);
@@ -180,7 +198,7 @@ public:
bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
- std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
+ std::shared_ptr<Kernel::KReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
void SignalStyleSetChangedEvent(u32 npad_id) const;
// Adds a new controller at an index.
@@ -195,6 +213,9 @@ public:
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
bool IsSixAxisSensorAtRest() const;
void SetSixAxisEnabled(bool six_axis_status);
+ void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2);
+ std::pair<f32, f32> GetSixAxisFusionParameters();
+ void ResetSixAxisFusionParameters();
LedPattern GetLedPattern(u32 npad_id);
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
@@ -228,12 +249,32 @@ private:
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+ enum class ColorAttributes : u32_le {
+ Ok = 0,
+ ReadError = 1,
+ NoController = 2,
+ };
+ static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size");
+
struct ControllerColor {
- u32_le body_color;
- u32_le button_color;
+ u32_le body;
+ u32_le button;
};
static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
+ struct FullKeyColor {
+ ColorAttributes attribute;
+ ControllerColor fullkey;
+ };
+ static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size");
+
+ struct JoyconColor {
+ ColorAttributes attribute;
+ ControllerColor left;
+ ControllerColor right;
+ };
+ static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size");
+
struct ControllerPadState {
union {
u64_le raw{};
@@ -275,6 +316,9 @@ private:
BitField<26, 1, u64> right_sl;
BitField<27, 1, u64> right_sr;
+
+ BitField<28, 1, u64> palma;
+ BitField<30, 1, u64> handheld_left_b;
};
};
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -288,12 +332,12 @@ private:
struct ConnectionState {
union {
u32_le raw{};
- BitField<0, 1, u32> IsConnected;
- BitField<1, 1, u32> IsWired;
- BitField<2, 1, u32> IsLeftJoyConnected;
- BitField<3, 1, u32> IsLeftJoyWired;
- BitField<4, 1, u32> IsRightJoyConnected;
- BitField<5, 1, u32> IsRightJoyWired;
+ BitField<0, 1, u32> is_connected;
+ BitField<1, 1, u32> is_wired;
+ BitField<2, 1, u32> is_left_connected;
+ BitField<3, 1, u32> is_left_wired;
+ BitField<4, 1, u32> is_right_connected;
+ BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
@@ -319,6 +363,15 @@ private:
};
static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
+ struct SixAxisAttributes {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32> is_connected;
+ BitField<1, 1, u32> is_interpolated;
+ };
+ };
+ static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size");
+
struct SixAxisStates {
s64_le timestamp{};
INSERT_PADDING_WORDS(2);
@@ -327,7 +380,8 @@ private:
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
- s64_le always_one{1};
+ SixAxisAttributes attribute;
+ INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
@@ -337,32 +391,73 @@ private:
};
static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
- enum class ColorReadError : u32_le {
- ReadOk = 0,
- ColorDoesntExist = 1,
- NoController = 2,
+ struct TriggerState {
+ s64_le timestamp{};
+ s64_le timestamp2{};
+ s32_le l_analog{};
+ s32_le r_analog{};
};
+ static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size");
- struct NPadProperties {
+ struct TriggerGeneric {
+ INSERT_PADDING_BYTES(0x4);
+ s64_le timestamp;
+ INSERT_PADDING_BYTES(0x4);
+ s64_le total_entry_count;
+ s64_le last_entry_index;
+ s64_le entry_count;
+ std::array<TriggerState, 17> trigger{};
+ };
+ static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
+
+ struct NPadSystemProperties {
union {
s64_le raw{};
+ BitField<0, 1, s64> is_charging_joy_dual;
+ BitField<1, 1, s64> is_charging_joy_left;
+ BitField<2, 1, s64> is_charging_joy_right;
+ BitField<3, 1, s64> is_powered_joy_dual;
+ BitField<4, 1, s64> is_powered_joy_left;
+ BitField<5, 1, s64> is_powered_joy_right;
+ BitField<9, 1, s64> is_system_unsupported_button;
+ BitField<10, 1, s64> is_system_ext_unsupported_button;
BitField<11, 1, s64> is_vertical;
BitField<12, 1, s64> is_horizontal;
BitField<13, 1, s64> use_plus;
BitField<14, 1, s64> use_minus;
+ BitField<15, 1, s64> use_directional_buttons;
+ };
+ };
+ static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
+
+ struct NPadButtonProperties {
+ union {
+ s32_le raw{};
+ BitField<0, 1, s32> is_home_button_protection_enabled;
};
};
+ static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
struct NPadDevice {
union {
u32_le raw{};
- BitField<0, 1, s32> pro_controller;
- BitField<1, 1, s32> handheld;
+ BitField<0, 1, s32> fullkey;
+ BitField<1, 1, s32> debug_pad;
BitField<2, 1, s32> handheld_left;
BitField<3, 1, s32> handheld_right;
BitField<4, 1, s32> joycon_left;
BitField<5, 1, s32> joycon_right;
- BitField<6, 1, s32> pokeball;
+ BitField<6, 1, s32> palma;
+ BitField<7, 1, s32> lark_hvc_left;
+ BitField<8, 1, s32> lark_hvc_right;
+ BitField<9, 1, s32> lark_nes_left;
+ BitField<10, 1, s32> lark_nes_right;
+ BitField<11, 1, s32> handheld_lark_hvc_left;
+ BitField<12, 1, s32> handheld_lark_hvc_right;
+ BitField<13, 1, s32> handheld_lark_nes_left;
+ BitField<14, 1, s32> handheld_lark_nes_right;
+ BitField<15, 1, s32> lucia;
+ BitField<31, 1, s32> system;
};
};
@@ -373,37 +468,71 @@ private:
std::array<Common::Vec3f, 3> orientation;
};
- struct NPadEntry {
- NpadStyleSet joy_styles;
- NpadAssignments pad_assignment;
+ struct NfcXcdHandle {
+ INSERT_PADDING_BYTES(0x60);
+ };
- ColorReadError single_color_error;
- ControllerColor single_color;
+ struct AppletFooterUiAttributes {
+ INSERT_PADDING_BYTES(0x4);
+ };
+
+ enum class AppletFooterUiType : u8 {
+ None = 0,
+ HandheldNone = 1,
+ HandheldJoyConLeftOnly = 1,
+ HandheldJoyConRightOnly = 3,
+ HandheldJoyConLeftJoyConRight = 4,
+ JoyDual = 5,
+ JoyDualLeftOnly = 6,
+ JoyDualRightOnly = 7,
+ JoyLeftHorizontal = 8,
+ JoyLeftVertical = 9,
+ JoyRightHorizontal = 10,
+ JoyRightVertical = 11,
+ SwitchProController = 12,
+ CompatibleProController = 13,
+ CompatibleJoyCon = 14,
+ LarkHvc1 = 15,
+ LarkHvc2 = 16,
+ LarkNesLeft = 17,
+ LarkNesRight = 18,
+ Lucia = 19,
+ Verification = 20,
+ };
- ColorReadError dual_color_error;
- ControllerColor left_color;
- ControllerColor right_color;
+ struct NPadEntry {
+ NpadStyleSet style_set;
+ NpadAssignments assignment_mode;
+ FullKeyColor fullkey_color;
+ JoyconColor joycon_color;
- NPadGeneric main_controller_states;
+ NPadGeneric fullkey_states;
NPadGeneric handheld_states;
- NPadGeneric dual_states;
- NPadGeneric left_joy_states;
- NPadGeneric right_joy_states;
- NPadGeneric pokeball_states;
- NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
- // relying on this for the time being
- SixAxisGeneric sixaxis_full;
+ NPadGeneric joy_dual_states;
+ NPadGeneric joy_left_states;
+ NPadGeneric joy_right_states;
+ NPadGeneric palma_states;
+ NPadGeneric system_ext_states;
+ SixAxisGeneric sixaxis_fullkey;
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);
- std::array<u32, 3> battery_level;
- INSERT_PADDING_BYTES(0x5c);
- INSERT_PADDING_BYTES(0xdf8);
+ INSERT_PADDING_BYTES(0x4); // reserved
+ NPadSystemProperties system_properties;
+ NPadButtonProperties button_properties;
+ u32 battery_level_dual;
+ u32 battery_level_left;
+ u32 battery_level_right;
+ AppletFooterUiAttributes footer_attributes;
+ AppletFooterUiType footer_type;
+ // nfc_states needs to be checked switchbrew does not match with HW
+ NfcXcdHandle nfc_states;
+ INSERT_PADDING_BYTES(0x8); // Mutex
+ TriggerGeneric gc_trigger_states;
+ INSERT_PADDING_BYTES(0xc1f);
};
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
@@ -439,10 +568,9 @@ private:
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
- // NpadCommunicationMode is unknown, default value is 1
- NpadCommunicationMode communication_mode{NpadCommunicationMode::Unknown1};
+ NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
// Each controller should have their own styleset changed event
- std::array<Kernel::EventPair, 10> styleset_changed_events;
+ std::array<std::shared_ptr<Kernel::KEvent>, 10> styleset_changed_events;
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};
@@ -451,8 +579,11 @@ private:
std::array<bool, 10> unintended_home_button_input_protection{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
bool sixaxis_sensors_enabled{true};
+ f32 sixaxis_fusion_parameter1{};
+ f32 sixaxis_fusion_parameter2{};
bool sixaxis_at_rest{true};
std::array<ControllerPad, 10> npad_pad_states{};
+ std::array<TriggerState, 10> npad_trigger_states{};
bool is_in_lr_assignment_mode{false};
Core::System& system;
};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 0df395e85..5219f2dad 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
@@ -16,7 +17,13 @@ constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
Controller_Touchscreen::~Controller_Touchscreen() = default;
-void Controller_Touchscreen::OnInit() {}
+void Controller_Touchscreen::OnInit() {
+ for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
+ mouse_finger_id[id] = MAX_FINGERS;
+ keyboard_finger_id[id] = MAX_FINGERS;
+ udp_finger_id[id] = MAX_FINGERS;
+ }
+}
void Controller_Touchscreen::OnRelease() {}
@@ -40,38 +47,106 @@ 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;
- 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();
+ const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
+ const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
+ for (std::size_t id = 0; id < mouse_status.size(); ++id) {
+ mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
+ udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
}
- 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.GetCPUTicks();
- touch_entry.delta_time = tick - last_touch;
- last_touch = tick;
- touch_entry.finger = Settings::values.touchscreen.finger;
- cur_entry.entry_count = 1;
- } else {
- cur_entry.entry_count = 0;
+
+ if (Settings::values.use_touch_from_button) {
+ const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
+ for (std::size_t id = 0; id < mouse_status.size(); ++id) {
+ keyboard_finger_id[id] =
+ UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
+ }
}
+ std::array<Finger, 16> active_fingers;
+ const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
+ [](const auto& finger) { return finger.pressed; });
+ const auto active_fingers_count =
+ static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
+
+ const u64 tick = core_timing.GetCPUTicks();
+ cur_entry.entry_count = static_cast<s32_le>(active_fingers_count);
+ for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
+ auto& touch_entry = cur_entry.states[id];
+ if (id < active_fingers_count) {
+ touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width);
+ touch_entry.y = static_cast<u16>(active_fingers[id].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;
+ touch_entry.delta_time = tick - active_fingers[id].last_touch;
+ fingers[active_fingers[id].id].last_touch = tick;
+ touch_entry.finger = active_fingers[id].id;
+ touch_entry.attribute.raw = active_fingers[id].attribute.raw;
+ } else {
+ // Clear touch entry
+ touch_entry.attribute.raw = 0;
+ touch_entry.x = 0;
+ touch_entry.y = 0;
+ touch_entry.diameter_x = 0;
+ touch_entry.diameter_y = 0;
+ touch_entry.rotation_angle = 0;
+ touch_entry.delta_time = 0;
+ touch_entry.finger = 0;
+ }
+ }
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
}
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();
+ touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
+ touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
+ touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
+}
+
+std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
+ std::size_t first_free_id = 0;
+ while (first_free_id < MAX_FINGERS) {
+ if (!fingers[first_free_id].pressed) {
+ return first_free_id;
+ } else {
+ first_free_id++;
+ }
+ }
+ return std::nullopt;
+}
+
+std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
+ const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
+ const auto& [x, y, pressed] = touch_input;
+ if (pressed) {
+ Attributes attribute{};
+ if (finger_id == MAX_FINGERS) {
+ const auto first_free_id = GetUnusedFingerID();
+ if (!first_free_id) {
+ // Invalid finger id do nothing
+ return MAX_FINGERS;
+ }
+ finger_id = first_free_id.value();
+ fingers[finger_id].pressed = true;
+ fingers[finger_id].id = static_cast<u32_le>(finger_id);
+ attribute.start_touch.Assign(1);
+ }
+ fingers[finger_id].x = x;
+ fingers[finger_id].y = y;
+ fingers[finger_id].attribute = attribute;
+ return finger_id;
}
+
+ if (finger_id != MAX_FINGERS) {
+ if (!fingers[finger_id].attribute.end_touch) {
+ fingers[finger_id].attribute.end_touch.Assign(1);
+ fingers[finger_id].attribute.start_touch.Assign(0);
+ return finger_id;
+ }
+ fingers[finger_id].pressed = false;
+ }
+
+ return MAX_FINGERS;
}
+
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 4d9042adc..784124e25 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -30,6 +30,18 @@ public:
void OnLoadInputDevices() override;
private:
+ static constexpr std::size_t MAX_FINGERS = 16;
+
+ // Returns an unused finger id, if there is no fingers available std::nullopt will be returned
+ std::optional<std::size_t> GetUnusedFingerID() const;
+
+ // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
+ // changes will be made. Updates the coordinates if the finger id it's already set. If the touch
+ // ends delays the output by one frame to set the end_touch flag before finally freeing the
+ // finger id
+ std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
+ std::size_t finger_id);
+
struct Attributes {
union {
u32 raw{};
@@ -55,7 +67,7 @@ private:
s64_le sampling_number;
s64_le sampling_number2;
s32_le entry_count;
- std::array<TouchState, 16> states;
+ std::array<TouchState, MAX_FINGERS> states;
};
static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
@@ -66,9 +78,23 @@ private:
};
static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
"TouchScreenSharedMemory is an invalid size");
+
+ struct Finger {
+ u64_le last_touch{};
+ float x{};
+ float y{};
+ u32_le id{};
+ bool pressed{};
+ Attributes attribute;
+ };
+
TouchScreenSharedMemory shared_memory{};
- std::unique_ptr<Input::TouchDevice> touch_device;
+ std::unique_ptr<Input::TouchDevice> touch_mouse_device;
+ std::unique_ptr<Input::TouchDevice> touch_udp_device;
std::unique_ptr<Input::TouchDevice> touch_btn_device;
- s64_le last_touch{};
+ std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
+ std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
+ std::array<std::size_t, MAX_FINGERS> udp_finger_id;
+ std::array<Finger, MAX_FINGERS> fingers;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index ad229787c..5b59961bd 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -4,6 +4,7 @@
#pragma once
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -28,6 +29,67 @@ public:
void OnLoadInputDevices() override;
private:
+ struct Attributes {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32> is_connected;
+ BitField<1, 1, u32> is_wired;
+ BitField<2, 1, u32> is_left_connected;
+ BitField<3, 1, u32> is_left_wired;
+ BitField<4, 1, u32> is_right_connected;
+ BitField<5, 1, u32> is_right_wired;
+ };
+ };
+ static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size");
+
+ struct Buttons {
+ union {
+ u32_le raw{};
+ // Button states
+ BitField<0, 1, u32> a;
+ BitField<1, 1, u32> b;
+ BitField<2, 1, u32> x;
+ BitField<3, 1, u32> y;
+ BitField<4, 1, u32> l_stick;
+ BitField<5, 1, u32> r_stick;
+ BitField<6, 1, u32> l;
+ BitField<7, 1, u32> r;
+ BitField<8, 1, u32> zl;
+ BitField<9, 1, u32> zr;
+ BitField<10, 1, u32> plus;
+ BitField<11, 1, u32> minus;
+
+ // D-Pad
+ BitField<12, 1, u32> d_left;
+ BitField<13, 1, u32> d_up;
+ BitField<14, 1, u32> d_right;
+ BitField<15, 1, u32> d_down;
+
+ // Left JoyStick
+ BitField<16, 1, u32> l_stick_left;
+ BitField<17, 1, u32> l_stick_up;
+ BitField<18, 1, u32> l_stick_right;
+ BitField<19, 1, u32> l_stick_down;
+
+ // Right JoyStick
+ BitField<20, 1, u32> r_stick_left;
+ BitField<21, 1, u32> r_stick_up;
+ BitField<22, 1, u32> r_stick_right;
+ BitField<23, 1, u32> r_stick_down;
+
+ // Not always active?
+ BitField<24, 1, u32> left_sl;
+ BitField<25, 1, u32> left_sr;
+
+ BitField<26, 1, u32> right_sl;
+ BitField<27, 1, u32> right_sr;
+
+ BitField<28, 1, u32> palma;
+ BitField<30, 1, u32> handheld_left_b;
+ };
+ };
+ static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size");
+
struct AnalogStick {
s32_le x;
s32_le y;
@@ -37,10 +99,10 @@ private:
struct XPadState {
s64_le sampling_number;
s64_le sampling_number2;
- s32_le attributes;
- u32_le pad_states;
- AnalogStick x_stick;
- AnalogStick y_stick;
+ Attributes attributes;
+ Buttons pad_states;
+ AnalogStick l_stick;
+ AnalogStick r_stick;
};
static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8d95f74e6..1e2677320 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -14,10 +14,10 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.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"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/irs.h"
@@ -59,20 +59,26 @@ IAppletResource::IAppletResource(Core::System& system_)
MakeController<Controller_Mouse>(HidController::Mouse);
MakeController<Controller_Keyboard>(HidController::Keyboard);
MakeController<Controller_XPad>(HidController::XPad);
- MakeController<Controller_Stubbed>(HidController::Unknown1);
- MakeController<Controller_Stubbed>(HidController::Unknown2);
- MakeController<Controller_Stubbed>(HidController::Unknown3);
- MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
+ MakeController<Controller_Stubbed>(HidController::HomeButton);
+ MakeController<Controller_Stubbed>(HidController::SleepButton);
+ MakeController<Controller_Stubbed>(HidController::CaptureButton);
+ MakeController<Controller_Stubbed>(HidController::InputDetector);
+ MakeController<Controller_Stubbed>(HidController::UniquePad);
MakeController<Controller_NPad>(HidController::NPad);
MakeController<Controller_Gesture>(HidController::Gesture);
+ MakeController<Controller_Stubbed>(HidController::ConsoleSixAxisSensor);
// Homebrew doesn't try to activate some controllers, so we activate them by default
GetController<Controller_NPad>(HidController::NPad).ActivateController();
GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
- GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
- GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
- GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
+ GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
+ GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
+ GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
+ GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200);
+ GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
+ GetController<Controller_Stubbed>(HidController::ConsoleSixAxisSensor)
+ .SetCommonHeaderOffset(0x3C200);
// Register update callbacks
pad_update_event = Core::Timing::CreateEvent(
@@ -104,6 +110,7 @@ void IAppletResource::DeactivateController(HidController controller) {
IAppletResource ::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
+ system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
}
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
@@ -126,14 +133,23 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
+ // If ns_late is higher than the update rate ignore the delay
+ if (ns_late > motion_update_ns) {
+ ns_late = {};
+ }
+
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);
+ controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(
+ core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
+
+ // If ns_late is higher than the update rate ignore the delay
+ if (ns_late > motion_update_ns) {
+ ns_late = {};
}
core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
@@ -209,9 +225,9 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
{68, nullptr, "IsSixAxisSensorFusionEnabled"},
{69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
- {70, nullptr, "SetSixAxisSensorFusionParameters"},
- {71, nullptr, "GetSixAxisSensorFusionParameters"},
- {72, nullptr, "ResetSixAxisSensorFusionParameters"},
+ {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
+ {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
+ {72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
{73, nullptr, "SetAccelerometerParameters"},
{74, nullptr, "GetAccelerometerParameters"},
{75, nullptr, "ResetAccelerometerParameters"},
@@ -401,9 +417,9 @@ void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 basic_xpad_id{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ u32 basic_xpad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -431,9 +447,9 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -452,9 +468,9 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -473,9 +489,9 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -494,9 +510,9 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -515,11 +531,12 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
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{};
+ bool enable_sixaxis_sensor_fusion;
+ INSERT_PADDING_BYTES_NOINIT(3);
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -534,6 +551,83 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
+void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ f32 parameter1;
+ f32 parameter2;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2);
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
+ "parameter2={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.parameter1,
+ parameters.parameter2, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ f32 parameter1 = 0;
+ f32 parameter2 = 0;
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ std::tie(parameter1, parameter2) =
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetSixAxisFusionParameters();
+
+ 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, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(parameter1);
+ rb.Push(parameter2);
+}
+
+void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .ResetSixAxisFusionParameters();
+
+ 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::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
@@ -556,9 +650,9 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -577,9 +671,9 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -599,9 +693,9 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -620,9 +714,9 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 unknown{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ u32 unknown;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -702,10 +796,10 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
- u64 unknown{};
+ u32 npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ u64 unknown;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -722,9 +816,9 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ u32 npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -756,9 +850,9 @@ 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{};
+ u32 unknown;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -800,9 +894,9 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ u32 npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -821,10 +915,10 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
- u64 npad_joy_device_type{};
+ u32 npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ u64 npad_joy_device_type;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -844,9 +938,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ u32 npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -952,9 +1046,9 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ u32 npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -971,10 +1065,10 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
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{};
+ bool unintended_home_button_input_protection;
+ INSERT_PADDING_BYTES_NOINIT(3);
+ u32 npad_id;
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1026,10 +1120,10 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle vibration_device_handle{};
- Controller_NPad::VibrationValue vibration_value{};
- INSERT_PADDING_WORDS(1);
- u64 applet_resource_user_id{};
+ Controller_NPad::DeviceHandle vibration_device_handle;
+ Controller_NPad::VibrationValue vibration_value;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1050,9 +1144,9 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle vibration_device_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1147,9 +1241,9 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle vibration_device_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1180,9 +1274,9 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
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{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1200,9 +1294,9 @@ void Hid::StartConsoleSixAxisSensor(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{};
+ Controller_NPad::DeviceHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index b87bfdde1..7cc0433e2 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -29,12 +29,14 @@ enum class HidController : std::size_t {
Mouse,
Keyboard,
XPad,
- Unknown1,
- Unknown2,
- Unknown3,
- SixAxisSensor,
+ HomeButton,
+ SleepButton,
+ CaptureButton,
+ InputDetector,
+ UniquePad,
NPad,
Gesture,
+ ConsoleSixAxisSensor,
MaxControllers,
};
@@ -97,6 +99,9 @@ private:
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
+ void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
+ void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
+ void ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index 6ad3a2877..f4490f3d9 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -20,30 +20,30 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "SaveCurrentSetting"},
{1, nullptr, "LoadCurrentSetting"},
- {2, nullptr, "SetCurrentBrightnessSetting"},
- {3, nullptr, "GetCurrentBrightnessSetting"},
+ {2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"},
+ {3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"},
{4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
{5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
- {6, nullptr, "SwitchBacklightOn"},
- {7, nullptr, "SwitchBacklightOff"},
- {8, nullptr, "GetBacklightSwitchStatus"},
- {9, nullptr, "EnableDimming"},
- {10, nullptr, "DisableDimming"},
- {11, nullptr, "IsDimmingEnabled"},
- {12, nullptr, "EnableAutoBrightnessControl"},
- {13, nullptr, "DisableAutoBrightnessControl"},
- {14, nullptr, "IsAutoBrightnessControlEnabled"},
- {15, nullptr, "SetAmbientLightSensorValue"},
- {16, nullptr, "GetAmbientLightSensorValue"},
- {17, nullptr, "SetBrightnessReflectionDelayLevel"},
- {18, nullptr, "GetBrightnessReflectionDelayLevel"},
- {19, nullptr, "SetCurrentBrightnessMapping"},
- {20, nullptr, "GetCurrentBrightnessMapping"},
- {21, nullptr, "SetCurrentAmbientLightSensorMapping"},
- {22, nullptr, "GetCurrentAmbientLightSensorMapping"},
- {23, nullptr, "IsAmbientLightSensorAvailable"},
- {24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
- {25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
+ {6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"},
+ {7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"},
+ {8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"},
+ {9, &LBL::EnableDimming, "EnableDimming"},
+ {10, &LBL::DisableDimming, "DisableDimming"},
+ {11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"},
+ {12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"},
+ {13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"},
+ {14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"},
+ {15, &LBL::SetAmbientLightSensorValue, "SetAmbientLightSensorValue"},
+ {16, &LBL::GetAmbientLightSensorValue, "GetAmbientLightSensorValue"},
+ {17, &LBL::SetBrightnessReflectionDelayLevel, "SetBrightnessReflectionDelayLevel"},
+ {18, &LBL::GetBrightnessReflectionDelayLevel, "GetBrightnessReflectionDelayLevel"},
+ {19, &LBL::SetCurrentBrightnessMapping, "SetCurrentBrightnessMapping"},
+ {20, &LBL::GetCurrentBrightnessMapping, "GetCurrentBrightnessMapping"},
+ {21, &LBL::SetCurrentAmbientLightSensorMapping, "SetCurrentAmbientLightSensorMapping"},
+ {22, &LBL::GetCurrentAmbientLightSensorMapping, "GetCurrentAmbientLightSensorMapping"},
+ {23, &LBL::IsAmbientLightSensorAvailable, "IsAmbientLightSensorAvailable"},
+ {24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"},
+ {25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"},
{26, &LBL::EnableVrMode, "EnableVrMode"},
{27, &LBL::DisableVrMode, "DisableVrMode"},
{28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
@@ -55,6 +55,237 @@ public:
}
private:
+ enum class BacklightSwitchStatus : u32 {
+ Off = 0,
+ On = 1,
+ };
+
+ void SetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto brightness = rp.Pop<float>();
+
+ if (!std::isfinite(brightness)) {
+ LOG_ERROR(Service_LBL, "Brightness is infinite!");
+ brightness = 0.0f;
+ }
+
+ LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
+
+ current_brightness = brightness;
+ update_instantly = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto brightness = current_brightness;
+ if (!std::isfinite(brightness)) {
+ LOG_ERROR(Service_LBL, "Brightness is infinite!");
+ brightness = 0.0f;
+ }
+
+ LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(brightness);
+ }
+
+ void SwitchBacklightOn(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto fade_time = rp.Pop<u64_le>();
+ LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);
+
+ backlight_enabled = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SwitchBacklightOff(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto fade_time = rp.Pop<u64_le>();
+ LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);
+
+ backlight_enabled = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetBacklightSwitchStatus(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum<BacklightSwitchStatus>(backlight_enabled ? BacklightSwitchStatus::On
+ : BacklightSwitchStatus::Off);
+ }
+
+ void EnableDimming(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
+ dimming = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void DisableDimming(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
+ dimming = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void IsDimmingEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(dimming);
+ }
+
+ void EnableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+ auto_brightness = true;
+ update_instantly = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void DisableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+ auto_brightness = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void IsAutoBrightnessControlEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(auto_brightness);
+ }
+
+ void SetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto light_value = rp.Pop<float>();
+
+ LOG_DEBUG(Service_LBL, "called light_value={}", light_value);
+
+ ambient_light_value = light_value;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(ambient_light_value);
+ }
+
+ void SetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) {
+ // This is Intentional, this function does absolutely nothing
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) {
+ // This is intentional, the function is hard coded to return 0.0f on hardware
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(0.0f);
+ }
+
+ void SetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) {
+ // This is Intentional, this function does absolutely nothing
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) {
+ // This is Intentional, this function does absolutely nothing
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ // This function is suppose to return something but it seems like it doesn't
+ }
+
+ void SetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) {
+ // This is Intentional, this function does absolutely nothing
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) {
+ // This is Intentional, this function does absolutely nothing
+ LOG_DEBUG(Service_LBL, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ // This function is suppose to return something but it seems like it doesn't
+ }
+
+ void IsAmbientLightSensorAvailable(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LBL, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ // TODO(ogniK): Only return true if there's no device error
+ rb.Push(true);
+ }
+
+ void SetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto brightness = rp.Pop<float>();
+
+ if (!std::isfinite(brightness)) {
+ LOG_ERROR(Service_LBL, "Brightness is infinite!");
+ brightness = 0.0f;
+ }
+
+ LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
+
+ current_vr_brightness = brightness;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto brightness = current_vr_brightness;
+ if (!std::isfinite(brightness)) {
+ LOG_ERROR(Service_LBL, "Brightness is infinite!");
+ brightness = 0.0f;
+ }
+
+ LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(brightness);
+ }
+
void EnableVrMode(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
@@ -82,6 +313,14 @@ private:
}
bool vr_mode_enabled = false;
+ float current_brightness = 1.0f;
+ float backlight_brightness = 1.0f;
+ float ambient_light_value = 0.0f;
+ float current_vr_brightness = 1.0f;
+ bool dimming = true;
+ bool backlight_enabled = true;
+ bool update_instantly = false;
+ bool auto_brightness = false; // TODO(ogniK): Move to system settings
};
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h
new file mode 100644
index 000000000..a718c5c66
--- /dev/null
+++ b/src/core/hle/service/ldn/errors.h
@@ -0,0 +1,13 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::LDN {
+
+constexpr ResultCode ERROR_DISABLED{ErrorModule::LDN, 22};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index ee908f399..c630d93cd 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -6,6 +6,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/result.h"
+#include "core/hle/service/ldn/errors.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/sm/sm.h"
@@ -103,7 +104,7 @@ public:
: ServiceFramework{system_, "IUserLocalCommunicationService"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "GetState"},
+ {0, &IUserLocalCommunicationService::GetState, "GetState"},
{1, nullptr, "GetNetworkInfo"},
{2, nullptr, "GetIpv4Address"},
{3, nullptr, "GetDisconnectReason"},
@@ -138,13 +139,38 @@ public:
RegisterHandlers(functions);
}
- void Initialize2(Kernel::HLERequestContext& ctx) {
+ void GetState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
- // Result success seem make this services start network and continue.
- // If we just pass result error then it will stop and maybe try again and again.
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ // Indicate a network error, as we do not actually emulate LDN
+ rb.Push(static_cast<u32>(State::Error));
+
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Initialize2(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LDN, "called");
+
+ is_initialized = true;
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_UNKNOWN);
+ rb.Push(RESULT_SUCCESS);
}
+
+private:
+ enum class State {
+ None,
+ Initialized,
+ AccessPointOpened,
+ AccessPointCreated,
+ StationOpened,
+ StationConnected,
+ Error,
+ };
+
+ bool is_initialized{};
};
class LDNS final : public ServiceFramework<LDNS> {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9da786b4e..c724d2554 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -11,10 +11,10 @@
#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/kernel/svc_results.h"
#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/service.h"
#include "core/loader/nro.h"
@@ -330,7 +330,7 @@ public:
const VAddr addr{GetRandomMapRegion(page_table, size)};
const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)};
- if (result == Kernel::ERR_INVALID_ADDRESS_STATE) {
+ if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
@@ -361,7 +361,7 @@ public:
const ResultCode result{
page_table.MapProcessCodeMemory(addr + nro_size, bss_addr, bss_size)};
- if (result == Kernel::ERR_INVALID_ADDRESS_STATE) {
+ if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 8e49b068c..7d7542fc2 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -5,22 +5,71 @@
#include <sstream>
#include <string>
+#include <optional>
+#include <unordered_map>
+#include <boost/container_hash/hash.hpp>
#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"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::LM {
+enum class LogSeverity : u8 {
+ Trace = 0,
+ Info = 1,
+ Warning = 2,
+ Error = 3,
+ Fatal = 4,
+};
+
+// To keep flags out of hashing as well as the payload size
+struct LogPacketHeaderEntry {
+ u64_le pid{};
+ u64_le tid{};
+ LogSeverity severity{};
+ u8 verbosity{};
+
+ auto operator<=>(const LogPacketHeaderEntry&) const = default;
+};
+} // namespace Service::LM
+
+namespace std {
+template <>
+struct hash<Service::LM::LogPacketHeaderEntry> {
+ std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
+ std::size_t seed{};
+ boost::hash_combine(seed, k.pid);
+ boost::hash_combine(seed, k.tid);
+ boost::hash_combine(seed, k.severity);
+ boost::hash_combine(seed, k.verbosity);
+ return seed;
+ };
+};
+} // namespace std
+
+namespace Service::LM {
+
+enum class LogDestination : u32 {
+ TargetManager = 1 << 0,
+ Uart = 1 << 1,
+ UartSleep = 1 << 2,
+ All = 0xffff,
+};
+DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
+
+enum class LogPacketFlags : u8 {
+ Head = 1 << 0,
+ Tail = 1 << 1,
+ LittleEndian = 1 << 2,
+};
+DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
class ILogger final : public ServiceFramework<ILogger> {
public:
- explicit ILogger(Core::System& system_)
- : ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()},
- memory{system_.Memory()} {
+ explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
static const FunctionInfo functions[] = {
{0, &ILogger::Log, "Log"},
{1, &ILogger::SetDestination, "SetDestination"},
@@ -30,54 +79,264 @@ public:
private:
void Log(Kernel::HLERequestContext& ctx) {
+ std::size_t offset{};
+ const auto data = ctx.ReadBuffer();
+
// This function only succeeds - Get that out of the way
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- // Read MessageHeader, despite not doing anything with it right now
- MessageHeader header{};
- VAddr addr{ctx.BufferDescriptorX()[0].Address()};
- const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
- memory.ReadBlock(addr, &header, sizeof(MessageHeader));
- addr += sizeof(MessageHeader);
-
- FieldMap fields;
- while (addr < end_addr) {
- const auto field = static_cast<Field>(memory.Read8(addr++));
- const auto length = memory.Read8(addr++);
+ if (data.size() < sizeof(LogPacketHeader)) {
+ LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
+ return;
+ }
- if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
- ++addr;
- }
+ LogPacketHeader header{};
+ std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
+ offset += sizeof(LogPacketHeader);
- SCOPE_EXIT({ addr += length; });
+ LogPacketHeaderEntry entry{
+ .pid = header.pid,
+ .tid = header.tid,
+ .severity = header.severity,
+ .verbosity = header.verbosity,
+ };
- if (field == Field::Skip) {
- continue;
+ if (True(header.flags & LogPacketFlags::Head)) {
+ std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
+ std::memcpy(tmp.data(), data.data() + offset, tmp.size());
+ entries[entry] = std::move(tmp);
+ } else {
+ // Append to existing entry
+ if (!entries.contains(entry)) {
+ LOG_ERROR(Service_LM, "Log entry does not exist!");
+ return;
}
+ std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
- std::vector<u8> data(length);
- memory.ReadBlock(addr, data.data(), length);
- fields.emplace(field, std::move(data));
+ auto& existing_entry = entries[entry];
+ const auto base = existing_entry.size();
+ existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
+ std::memcpy(existing_entry.data() + base, data.data() + offset,
+ (data.size() - sizeof(LogPacketHeader)));
}
- manager.Log({header, std::move(fields)});
+ if (True(header.flags & LogPacketFlags::Tail)) {
+ auto it = entries.find(entry);
+ if (it == entries.end()) {
+ LOG_ERROR(Service_LM, "Log entry does not exist!");
+ return;
+ }
+ ParseLog(it->first, it->second);
+ entries.erase(it);
+ }
}
void SetDestination(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto destination = rp.PopEnum<DestinationFlag>();
-
- LOG_DEBUG(Service_LM, "called, destination={:08X}", destination);
+ const auto log_destination = rp.PopEnum<LogDestination>();
- manager.SetDestination(destination);
+ LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
+ destination = log_destination;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
- Manager& manager;
- Core::Memory::Memory& memory;
+ u64 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
+ u64 result{};
+ u32 shift{};
+
+ for (std::size_t i = 0; i < sizeof(u64); i++) {
+ const auto v = data[offset];
+ result |= (static_cast<u64>(v & 0x7f) << shift);
+ shift += 7;
+ offset++;
+ if (offset >= data.size() || ((v & 0x80) == 0)) {
+ break;
+ }
+ }
+ return result;
+ }
+
+ std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
+ std::size_t length) {
+ if (length == 0) {
+ return std::nullopt;
+ }
+ const auto length_to_read = std::min(length, data.size() - offset);
+
+ std::string output(length_to_read, '\0');
+ std::memcpy(output.data(), data.data() + offset, length_to_read);
+ offset += length_to_read;
+ return output;
+ }
+
+ u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
+ ASSERT(length == sizeof(u32));
+ u32_le output{};
+ std::memcpy(&output, data.data() + offset, sizeof(u32));
+ offset += length;
+ return output;
+ }
+
+ u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
+ ASSERT(length == sizeof(u64));
+ u64_le output{};
+ std::memcpy(&output, data.data() + offset, sizeof(u64));
+ offset += length;
+ return output;
+ }
+
+ void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
+ // Possible entries
+ std::optional<std::string> text_log;
+ std::optional<u32> line_number;
+ std::optional<std::string> file_name;
+ std::optional<std::string> function_name;
+ std::optional<std::string> module_name;
+ std::optional<std::string> thread_name;
+ std::optional<u64> log_pack_drop_count;
+ std::optional<s64> user_system_clock;
+ std::optional<std::string> process_name;
+
+ std::size_t offset{};
+ while (offset < log_data.size()) {
+ const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
+ const auto chunk_size = ReadLeb128(log_data, offset);
+
+ switch (key) {
+ case LogDataChunkKey::LogSessionBegin:
+ case LogDataChunkKey::LogSessionEnd:
+ break;
+ case LogDataChunkKey::TextLog:
+ text_log = ReadString(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::LineNumber:
+ line_number = ReadAsU32(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::FileName:
+ file_name = ReadString(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::FunctionName:
+ function_name = ReadString(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::ModuleName:
+ module_name = ReadString(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::ThreadName:
+ thread_name = ReadString(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::LogPacketDropCount:
+ log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::UserSystemClock:
+ user_system_clock = ReadAsU64(log_data, offset, chunk_size);
+ break;
+ case LogDataChunkKey::ProcessName:
+ process_name = ReadString(log_data, offset, chunk_size);
+ break;
+ }
+ }
+
+ std::string output_log{};
+ if (process_name) {
+ output_log += fmt::format("Process: {}\n", *process_name);
+ }
+ if (module_name) {
+ output_log += fmt::format("Module: {}\n", *module_name);
+ }
+ if (file_name) {
+ output_log += fmt::format("File: {}\n", *file_name);
+ }
+ if (function_name) {
+ output_log += fmt::format("Function: {}\n", *function_name);
+ }
+ if (line_number && *line_number != 0) {
+ output_log += fmt::format("Line: {}\n", *line_number);
+ }
+ output_log += fmt::format("ProcessID: {:X}\n", entry.pid);
+ output_log += fmt::format("ThreadID: {:X}\n", entry.tid);
+
+ if (text_log) {
+ output_log += fmt::format("Log Text: {}\n", *text_log);
+ }
+
+ switch (entry.severity) {
+ case LogSeverity::Trace:
+ LOG_DEBUG(Service_LM, "LogManager TRACE ({}):\n{}", DestinationToString(destination),
+ output_log);
+ break;
+ case LogSeverity::Info:
+ LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
+ output_log);
+ break;
+ case LogSeverity::Warning:
+ LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
+ DestinationToString(destination), output_log);
+ break;
+ case LogSeverity::Error:
+ LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
+ output_log);
+ break;
+ case LogSeverity::Fatal:
+ LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
+ output_log);
+ break;
+ default:
+ LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
+ DestinationToString(destination), output_log);
+ break;
+ }
+ }
+
+ static std::string DestinationToString(LogDestination destination) {
+ if (True(destination & LogDestination::All)) {
+ return "TargetManager | Uart | UartSleep";
+ }
+ std::string output{};
+ if (True(destination & LogDestination::TargetManager)) {
+ output += "| TargetManager";
+ }
+ if (True(destination & LogDestination::Uart)) {
+ output += "| Uart";
+ }
+ if (True(destination & LogDestination::UartSleep)) {
+ output += "| UartSleep";
+ }
+ if (output.length() > 0) {
+ return output.substr(2);
+ }
+ return "No Destination";
+ }
+
+ enum class LogDataChunkKey : u32 {
+ LogSessionBegin = 0,
+ LogSessionEnd = 1,
+ TextLog = 2,
+ LineNumber = 3,
+ FileName = 4,
+ FunctionName = 5,
+ ModuleName = 6,
+ ThreadName = 7,
+ LogPacketDropCount = 8,
+ UserSystemClock = 9,
+ ProcessName = 10,
+ };
+
+ struct LogPacketHeader {
+ u64_le pid{};
+ u64_le tid{};
+ LogPacketFlags flags{};
+ INSERT_PADDING_BYTES(1);
+ LogSeverity severity{};
+ u8 verbosity{};
+ u32_le payload_size{};
+ };
+ static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
+
+ std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
+ LogDestination destination{LogDestination::All};
};
class LM final : public ServiceFramework<LM> {
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp
deleted file mode 100644
index 3ee2374e7..000000000
--- a/src/core/hle/service/lm/manager.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "common/string_util.h"
-#include "core/hle/service/lm/manager.h"
-#include "core/reporter.h"
-
-namespace Service::LM {
-
-std::ostream& operator<<(std::ostream& os, DestinationFlag dest) {
- std::vector<std::string> array;
- const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) {
- if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) {
- array.emplace_back(std::move(name));
- }
- };
-
- check_single_flag(DestinationFlag::Default, "Default");
- check_single_flag(DestinationFlag::UART, "UART");
- check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)");
-
- os << "[";
- for (const auto& entry : array) {
- os << entry << ", ";
- }
- return os << "]";
-}
-
-std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) {
- switch (severity) {
- case MessageHeader::Severity::Trace:
- return os << "Trace";
- case MessageHeader::Severity::Info:
- return os << "Info";
- case MessageHeader::Severity::Warning:
- return os << "Warning";
- case MessageHeader::Severity::Error:
- return os << "Error";
- case MessageHeader::Severity::Critical:
- return os << "Critical";
- default:
- return os << fmt::format("{:08X}", static_cast<u32>(severity));
- }
-}
-
-std::ostream& operator<<(std::ostream& os, Field field) {
- switch (field) {
- case Field::Skip:
- return os << "Skip";
- case Field::Message:
- return os << "Message";
- case Field::Line:
- return os << "Line";
- case Field::Filename:
- return os << "Filename";
- case Field::Function:
- return os << "Function";
- case Field::Module:
- return os << "Module";
- case Field::Thread:
- return os << "Thread";
- default:
- return os << fmt::format("{:08X}", static_cast<u32>(field));
- }
-}
-
-std::string FormatField(Field type, const std::vector<u8>& data) {
- switch (type) {
- case Field::Skip:
- return "";
- case Field::Line:
- if (data.size() >= sizeof(u32)) {
- u32 line;
- std::memcpy(&line, data.data(), sizeof(u32));
- return fmt::format("{}", line);
- }
- return "[ERROR DECODING LINE NUMBER]";
- case Field::Message:
- case Field::Filename:
- case Field::Function:
- case Field::Module:
- case Field::Thread:
- return Common::StringFromFixedZeroTerminatedBuffer(
- reinterpret_cast<const char*>(data.data()), data.size());
- default:
- UNIMPLEMENTED_MSG("Unimplemented field type={}", type);
- return "";
- }
-}
-
-Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {}
-
-Manager::~Manager() = default;
-
-void Manager::SetEnabled(bool enabled) {
- this->enabled = enabled;
-}
-
-void Manager::SetDestination(DestinationFlag destination) {
- this->destination = destination;
-}
-
-void Manager::Log(LogMessage message) {
- if (message.header.IsHeadLog()) {
- InitializeLog();
- }
-
- current_log.emplace_back(std::move(message));
-
- if (current_log.back().header.IsTailLog()) {
- FinalizeLog();
- }
-}
-
-void Manager::Flush() {
- FinalizeLog();
-}
-
-void Manager::InitializeLog() {
- current_log.clear();
-
- LOG_INFO(Service_LM, "Initialized new log session");
-}
-
-void Manager::FinalizeLog() {
- reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log));
-
- LOG_INFO(Service_LM, "Finalized current log session");
-}
-
-} // namespace Service::LM
diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h
deleted file mode 100644
index 544e636ba..000000000
--- a/src/core/hle/service/lm/manager.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <map>
-#include <ostream>
-#include <vector>
-#include "common/bit_field.h"
-#include "common/common_types.h"
-#include "common/swap.h"
-
-namespace Core {
-class Reporter;
-}
-
-namespace Service::LM {
-
-enum class DestinationFlag : u32 {
- Default = 1,
- UART = 2,
- UARTSleeping = 4,
-
- All = 0xFFFF,
-};
-
-struct MessageHeader {
- enum Flags : u32_le {
- IsHead = 1,
- IsTail = 2,
- };
- enum Severity : u32_le {
- Trace,
- Info,
- Warning,
- Error,
- Critical,
- };
-
- u64_le pid;
- u64_le thread_context;
- union {
- BitField<0, 16, Flags> flags;
- BitField<16, 8, Severity> severity;
- BitField<24, 8, u32> verbosity;
- };
- u32_le payload_size;
-
- bool IsHeadLog() const {
- return flags & IsHead;
- }
- bool IsTailLog() const {
- return flags & IsTail;
- }
-};
-static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
-
-enum class Field : u8 {
- Skip = 1,
- Message = 2,
- Line = 3,
- Filename = 4,
- Function = 5,
- Module = 6,
- Thread = 7,
-};
-
-std::ostream& operator<<(std::ostream& os, DestinationFlag dest);
-std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity);
-std::ostream& operator<<(std::ostream& os, Field field);
-
-using FieldMap = std::map<Field, std::vector<u8>>;
-
-struct LogMessage {
- MessageHeader header;
- FieldMap fields;
-};
-
-std::string FormatField(Field type, const std::vector<u8>& data);
-
-class Manager {
-public:
- explicit Manager(Core::Reporter& reporter);
- ~Manager();
-
- void SetEnabled(bool enabled);
- void SetDestination(DestinationFlag destination);
-
- void Log(LogMessage message);
-
- void Flush();
-
-private:
- void InitializeLog();
- void FinalizeLog();
-
- bool enabled = true;
- DestinationFlag destination = DestinationFlag::All;
-
- std::vector<LogMessage> current_log;
-
- Core::Reporter& reporter;
-};
-
-} // namespace Service::LM
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp
index d73b90015..70350a2a3 100644
--- a/src/core/hle/service/mii/manager.cpp
+++ b/src/core/hle/service/mii/manager.cpp
@@ -21,7 +21,7 @@ namespace {
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
-constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
+constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
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};
@@ -100,6 +100,7 @@ MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
.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()),
+ .padding = 0,
};
}
@@ -140,13 +141,6 @@ 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{};
@@ -192,32 +186,20 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
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(),
+ const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)};
+ const auto faceline_color_info{RawData::RandomMiiFacelineColor.at(
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))};
+ const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
+ const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
+ const auto hair_type_info{RawData::RandomMiiHairType.at(index)};
+ const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
+ static_cast<std::size_t>(age))};
+ const auto eye_type_info{RawData::RandomMiiEyeType.at(index)};
+ const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
+ const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
+ const auto nose_type_info{RawData::RandomMiiNoseType.at(index)};
+ const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)};
+ const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
bf.faceline_type.Assign(
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
@@ -454,8 +436,7 @@ MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
}
MiiInfo MiiManager::BuildDefault(std::size_t index) {
- return ConvertStoreDataToInfo(BuildDefaultStoreData(
- GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
+ return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
}
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
diff --git a/src/core/hle/service/mii/manager.h b/src/core/hle/service/mii/manager.h
index 927451dea..2106a528a 100644
--- a/src/core/hle/service/mii/manager.h
+++ b/src/core/hle/service/mii/manager.h
@@ -27,58 +27,58 @@ enum class SourceFlag : u32 {
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);
+ Common::UUID 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;
+ u8 padding;
std::u16string Name() const;
};
@@ -233,7 +233,7 @@ struct RandomMiiData4 {
Age age{};
Race race{};
u32 values_count{};
- std::array<u8, 0xbc> values{};
+ std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
@@ -241,14 +241,14 @@ struct RandomMiiData3 {
u32 arg_1;
u32 arg_2;
u32 values_count;
- std::array<u8, 0xbc> values{};
+ std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
struct RandomMiiData2 {
u32 arg_1;
u32 values_count;
- std::array<u8, 0xbc> values{};
+ std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
@@ -324,7 +324,7 @@ public:
ResultCode GetIndex(const MiiInfo& info, u32& index);
private:
- const Common::UUID user_id;
+ const Common::UUID user_id{Common::INVALID_UUID};
u64 update_counter{};
};
diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/raw_data.cpp
index 25d7bae0c..9d3c8017a 100644
--- a/src/core/hle/service/mii/raw_data.cpp
+++ b/src/core/hle/service/mii/raw_data.cpp
@@ -22,2240 +22,1642 @@
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<Service::Mii::DefaultMii, 8> DefaultMii{
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 0,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 33,
+ .hair_color = 1,
+ .hair_flip = 0,
+ .eye_type = 2,
+ .eye_color = 0,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 4,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 6,
+ .eyebrow_color = 1,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Male,
+ .favorite_color = 0,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 0,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 12,
+ .hair_color = 1,
+ .hair_flip = 0,
+ .eye_type = 4,
+ .eye_color = 0,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 3,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 0,
+ .eyebrow_color = 1,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Female,
+ .favorite_color = 0,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 4,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 68,
+ .hair_color = 0,
+ .hair_flip = 0,
+ .eye_type = 2,
+ .eye_color = 0,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 4,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 6,
+ .eyebrow_color = 0,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Male,
+ .favorite_color = 4,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 0,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 55,
+ .hair_color = 6,
+ .hair_flip = 0,
+ .eye_type = 2,
+ .eye_color = 4,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 4,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 6,
+ .eyebrow_color = 6,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Male,
+ .favorite_color = 5,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 1,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 33,
+ .hair_color = 1,
+ .hair_flip = 0,
+ .eye_type = 2,
+ .eye_color = 0,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 4,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 6,
+ .eyebrow_color = 1,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Male,
+ .favorite_color = 0,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 2,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 24,
+ .hair_color = 0,
+ .hair_flip = 0,
+ .eye_type = 4,
+ .eye_color = 0,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 3,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 0,
+ .eyebrow_color = 0,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Female,
+ .favorite_color = 2,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 0,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 14,
+ .hair_color = 7,
+ .hair_flip = 0,
+ .eye_type = 4,
+ .eye_color = 5,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 3,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 0,
+ .eyebrow_color = 7,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Female,
+ .favorite_color = 6,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
+ Service::Mii::DefaultMii{
+ .face_type = 0,
+ .face_color = 0,
+ .face_wrinkle = 0,
+ .face_makeup = 0,
+ .hair_type = 12,
+ .hair_color = 1,
+ .hair_flip = 0,
+ .eye_type = 4,
+ .eye_color = 0,
+ .eye_scale = 4,
+ .eye_aspect = 3,
+ .eye_rotate = 3,
+ .eye_x = 2,
+ .eye_y = 12,
+ .eyebrow_type = 0,
+ .eyebrow_color = 1,
+ .eyebrow_scale = 4,
+ .eyebrow_aspect = 3,
+ .eyebrow_rotate = 6,
+ .eyebrow_x = 2,
+ .eyebrow_y = 10,
+ .nose_type = 1,
+ .nose_scale = 4,
+ .nose_y = 9,
+ .mouth_type = 23,
+ .mouth_color = 0,
+ .mouth_scale = 4,
+ .mouth_aspect = 3,
+ .mouth_y = 13,
+ .mustache_type = 0,
+ .beard_type = 0,
+ .beard_color = 0,
+ .mustache_scale = 4,
+ .mustache_y = 10,
+ .glasses_type = 0,
+ .glasses_color = 0,
+ .glasses_scale = 4,
+ .glasses_y = 10,
+ .mole_type = 0,
+ .mole_scale = 4,
+ .mole_x = 2,
+ .mole_y = 20,
+ .height = 64,
+ .weight = 64,
+ .gender = Gender::Female,
+ .favorite_color = 7,
+ .region = 0,
+ .font_region = FontRegion::Standard,
+ .type = 0,
+ },
-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<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 10,
+ .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 10,
+ .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 10,
+ .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 12,
+ .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 13,
+ .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 12,
+ .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 12,
+ .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 13,
+ .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 12,
+ .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 10,
+ .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 10,
+ .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 10,
+ .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 12,
+ .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 12,
+ .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 12,
+ .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 12,
+ .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 12,
+ .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 12,
+ .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
+ },
+};
-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<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 0,
+ .arg_2 = 0,
+ .values_count = 10,
+ .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 0,
+ .arg_2 = 1,
+ .values_count = 10,
+ .values = {0, 0, 0, 0, 1, 1, 2, 3, 3, 3},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 0,
+ .arg_2 = 2,
+ .values_count = 10,
+ .values = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 1,
+ .arg_2 = 0,
+ .values_count = 10,
+ .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 1,
+ .arg_2 = 1,
+ .values_count = 10,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 3},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 1,
+ .arg_2 = 2,
+ .values_count = 10,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
+ },
+};
-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<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11},
+ },
+};
-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<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ },
+};
-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<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 30,
+ .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45,
+ 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 75, 76, 86, 89},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 31,
+ .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47,
+ 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 31,
+ .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47,
+ 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 38,
+ .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 40, 42, 43, 44, 45, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 75, 76, 86, 89},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 39,
+ .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 39,
+ .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 18,
+ .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 19,
+ .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 19,
+ .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 39,
+ .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 76, 77, 79, 80, 83, 85},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 42,
+ .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50,
+ 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 42,
+ .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50,
+ 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 44,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 42, 50,
+ 58, 60, 62, 63, 64, 69, 71, 76, 79, 80, 81, 82, 83, 86},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 44,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58,
+ 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 44,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58,
+ 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 24,
+ .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14,
+ 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 76, 83},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 27,
+ .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17,
+ 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 27,
+ .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17,
+ 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85},
+ },
+};
-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<RandomMiiData3, 9> RandomMiiHairColor{
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 0,
+ .arg_2 = 0,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 0,
+ .arg_2 = 1,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 0,
+ .arg_2 = 2,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 1,
+ .arg_2 = 0,
+ .values_count = 20,
+ .values = {2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 1,
+ .arg_2 = 1,
+ .values_count = 20,
+ .values = {2, 3, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 1,
+ .arg_2 = 2,
+ .values_count = 20,
+ .values = {2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 2,
+ .arg_2 = 0,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 2,
+ .arg_2 = 1,
+ .values_count = 20,
+ .values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3},
+ },
+ Service::Mii::RandomMiiData3{
+ .arg_1 = 2,
+ .arg_2 = 2,
+ .values_count = 20,
+ .values = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
+ },
+};
-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<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 26,
+ .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27,
+ 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 26,
+ .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27,
+ 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 27,
+ .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 26, 27,
+ 29, 32, 34, 36, 38, 39, 41, 43, 47, 48, 49, 53, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 35,
+ .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29,
+ 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 35,
+ .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29,
+ 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 35,
+ .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 21, 22, 26, 27, 29,
+ 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 48, 49, 50, 53, 56, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 30,
+ .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21,
+ 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 30,
+ .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21,
+ 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 30,
+ .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 22,
+ 26, 31, 32, 34, 36, 37, 39, 41, 44, 48, 49, 50, 51, 53, 57},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 39,
+ .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27,
+ 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 39,
+ .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27,
+ 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 40,
+ .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26,
+ 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 46,
+ .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17,
+ 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37,
+ 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 46,
+ .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17,
+ 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37,
+ 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 46,
+ .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18,
+ 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 37,
+ 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 34,
+ .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23,
+ 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 34,
+ .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23,
+ 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 35,
+ .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24,
+ 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47},
+ },
+};
-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<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor{
+ Service::Mii::RandomMiiData2{
+ .arg_1 = 0,
+ .values_count = 10,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+ },
+ Service::Mii::RandomMiiData2{
+ .arg_1 = 1,
+ .values_count = 10,
+ .values = {0, 1, 1, 2, 3, 3, 4, 4, 4, 5},
+ },
+ Service::Mii::RandomMiiData2{
+ .arg_1 = 2,
+ .values_count = 10,
+ .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+ },
+};
-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<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 18,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 18,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 18,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 23,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 23,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 23,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 21,
+ .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 21,
+ .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 21,
+ .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 9,
+ .values = {0, 1, 3, 7, 8, 9, 10, 11, 13},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 9,
+ .values = {0, 1, 3, 7, 8, 9, 10, 11, 13},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 9,
+ .values = {0, 1, 3, 7, 8, 9, 10, 11, 13},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 11,
+ .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 11,
+ .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 11,
+ .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 9,
+ .values = {0, 3, 7, 8, 9, 10, 11, 13, 15},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 9,
+ .values = {0, 3, 7, 8, 9, 10, 11, 13, 15},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 9,
+ .values = {0, 3, 7, 8, 9, 10, 11, 13, 15},
+ },
+};
-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<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 11,
+ .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 11,
+ .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 11,
+ .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 18,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 18,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 15,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 18,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 18,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 15,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 8,
+ .values = {0, 1, 3, 4, 8, 10, 13, 14},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 8,
+ .values = {0, 1, 3, 4, 8, 10, 13, 14},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 8,
+ .values = {0, 1, 3, 4, 8, 10, 13, 14},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 12,
+ .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 11,
+ .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 10,
+ .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 12,
+ .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 11,
+ .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 10,
+ .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14},
+ },
+};
-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};
+const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType{
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 25,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18,
+ 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 27,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17,
+ 18, 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 28,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17,
+ 18, 19, 21, 22, 23, 25, 26, 28, 30, 31, 32, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 24,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 26,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 26,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 24,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 26,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Male,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 26,
+ .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Black,
+ .values_count = 25,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15,
+ 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Black,
+ .values_count = 26,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
+ 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Black,
+ .values_count = 26,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
+ 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::White,
+ .values_count = 25,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15,
+ 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::White,
+ .values_count = 26,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
+ 15, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::White,
+ .values_count = 25,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
+ 15, 17, 18, 19, 21, 22, 23, 24, 25, 29, 33, 35},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Young,
+ .race = Race::Asian,
+ .values_count = 24,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14,
+ 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Normal,
+ .race = Race::Asian,
+ .values_count = 25,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
+ 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33},
+ },
+ Service::Mii::RandomMiiData4{
+ .gender = Gender::Female,
+ .age = Age::Old,
+ .race = Race::Asian,
+ .values_count = 25,
+ .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
+ 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33},
+ },
+};
+
+const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType{
+ Service::Mii::RandomMiiData2{
+ .arg_1 = 0,
+ .values_count = 9,
+ .values = {90, 94, 96, 100, 0, 0, 0, 0, 0},
+ },
+ Service::Mii::RandomMiiData2{
+ .arg_1 = 1,
+ .values_count = 9,
+ .values = {83, 86, 90, 93, 94, 96, 98, 100, 0},
+ },
+ Service::Mii::RandomMiiData2{
+ .arg_1 = 2,
+ .values_count = 9,
+ .values = {78, 83, 0, 93, 0, 0, 98, 100, 0},
+ },
+};
} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
index a02a5c0fd..0e35d69d2 100644
--- a/src/core/hle/service/mii/raw_data.h
+++ b/src/core/hle/service/mii/raw_data.h
@@ -7,21 +7,22 @@
#include <array>
#include "common/common_types.h"
+#include "core/hle/service/mii/manager.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;
+extern const std::array<Service::Mii::DefaultMii, 8> DefaultMii;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline;
+extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType;
+extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType;
+extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType;
+extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType;
+extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType;
} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 5557da72e..2d1d4d67f 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -8,10 +8,11 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
@@ -25,7 +26,8 @@ Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& syst
const char* name)
: ServiceFramework{system_, name}, module{std::move(module_)} {
auto& kernel = system.Kernel();
- nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:NFCTagDetected");
+ nfc_tag_load = Kernel::KEvent::Create(kernel, "IUser:NFCTagDetected");
+ nfc_tag_load->Initialize();
}
Module::Interface::~Interface() = default;
@@ -64,9 +66,10 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- deactivate_event = Kernel::WritableEvent::CreateEventPair(kernel, "IUser:DeactivateEvent");
- availability_change_event =
- Kernel::WritableEvent::CreateEventPair(kernel, "IUser:AvailabilityChangeEvent");
+ deactivate_event = Kernel::KEvent::Create(kernel, "IUser:DeactivateEvent");
+ deactivate_event->Initialize();
+ availability_change_event = Kernel::KEvent::Create(kernel, "IUser:AvailabilityChangeEvent");
+ availability_change_event->Initialize();
}
private:
@@ -164,7 +167,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(deactivate_event.readable);
+ rb.PushCopyObjects(deactivate_event->GetReadableEvent());
}
void StopDetection(Kernel::HLERequestContext& ctx) {
@@ -173,7 +176,7 @@ private:
switch (device_state) {
case DeviceState::TagFound:
case DeviceState::TagNearby:
- deactivate_event.writable->Signal();
+ deactivate_event->GetWritableEvent()->Signal();
device_state = DeviceState::Initialized;
break;
case DeviceState::SearchingForTag:
@@ -190,12 +193,6 @@ private:
void GetDeviceState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
- auto nfc_event = nfp_interface.GetNFCEvent();
- if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) {
- device_state = DeviceState::TagFound;
- nfc_event->Clear();
- }
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(device_state));
@@ -218,7 +215,7 @@ private:
const auto& amiibo = nfp_interface.GetAmiiboBuffer();
const TagInfo tag_info{
.uuid = amiibo.uuid,
- .uuid_length = static_cast<u8>(tag_info.uuid.size()),
+ .uuid_length = static_cast<u8>(amiibo.uuid.size()),
.padding_1 = {},
.protocol = 1, // TODO(ogniK): Figure out actual values
.tag_type = 2,
@@ -268,7 +265,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(availability_change_event.readable);
+ rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
@@ -322,8 +319,8 @@ private:
const u32 npad_id{0}; // Player 1 controller
State state{State::NonInitialized};
DeviceState device_state{DeviceState::Initialized};
- Kernel::EventPair deactivate_event;
- Kernel::EventPair availability_change_event;
+ std::shared_ptr<Kernel::KEvent> deactivate_event;
+ std::shared_ptr<Kernel::KEvent> availability_change_event;
const Module::Interface& nfp_interface;
};
@@ -342,12 +339,12 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
}
std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
- nfc_tag_load.writable->Signal();
+ nfc_tag_load->GetWritableEvent()->Signal();
return true;
}
-const std::shared_ptr<Kernel::ReadableEvent>& Module::Interface::GetNFCEvent() const {
- return nfc_tag_load.readable;
+const std::shared_ptr<Kernel::KReadableEvent>& Module::Interface::GetNFCEvent() const {
+ return nfc_tag_load->GetReadableEvent();
}
const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 295de535b..c46551760 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -6,10 +6,13 @@
#include <array>
#include <vector>
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
+
#include "core/hle/service/service.h"
+namespace Kernel {
+class KEvent;
+}
+
namespace Service::NFP {
class Module final {
@@ -35,11 +38,11 @@ public:
void CreateUserInterface(Kernel::HLERequestContext& ctx);
bool LoadAmiibo(const std::vector<u8>& buffer);
- const std::shared_ptr<Kernel::ReadableEvent>& GetNFCEvent() const;
+ const std::shared_ptr<Kernel::KReadableEvent>& GetNFCEvent() const;
const AmiiboFile& GetAmiiboBuffer() const;
private:
- Kernel::EventPair nfc_tag_load{};
+ std::shared_ptr<Kernel::KEvent> nfc_tag_load;
AmiiboFile amiibo{};
protected:
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index ef5176bea..afb3342d6 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -4,9 +4,9 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#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"
@@ -21,6 +21,93 @@ enum class RequestState : u32 {
Connected = 3,
};
+struct IpAddressSetting {
+ bool is_automatic{};
+ Network::IPv4Address current_address{};
+ Network::IPv4Address subnet_mask{};
+ Network::IPv4Address gateway{};
+};
+static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size.");
+
+struct DnsSetting {
+ bool is_automatic{};
+ Network::IPv4Address primary_dns{};
+ Network::IPv4Address secondary_dns{};
+};
+static_assert(sizeof(DnsSetting) == 0x9, "DnsSetting has incorrect size.");
+
+struct ProxySetting {
+ bool enabled{};
+ INSERT_PADDING_BYTES(1);
+ u16 port{};
+ std::array<char, 0x64> proxy_server{};
+ bool automatic_auth_enabled{};
+ std::array<char, 0x20> user{};
+ std::array<char, 0x20> password{};
+ INSERT_PADDING_BYTES(1);
+};
+static_assert(sizeof(ProxySetting) == 0xAA, "ProxySetting has incorrect size.");
+
+struct IpSettingData {
+ IpAddressSetting ip_address_setting{};
+ DnsSetting dns_setting{};
+ ProxySetting proxy_setting{};
+ u16 mtu{};
+};
+static_assert(sizeof(IpSettingData) == 0xC2, "IpSettingData has incorrect size.");
+
+struct SfWirelessSettingData {
+ u8 ssid_length{};
+ std::array<char, 0x20> ssid{};
+ u8 unknown_1{};
+ u8 unknown_2{};
+ u8 unknown_3{};
+ std::array<char, 0x41> passphrase{};
+};
+static_assert(sizeof(SfWirelessSettingData) == 0x65, "SfWirelessSettingData has incorrect size.");
+
+struct NifmWirelessSettingData {
+ u8 ssid_length{};
+ std::array<char, 0x21> ssid{};
+ u8 unknown_1{};
+ INSERT_PADDING_BYTES(1);
+ u32 unknown_2{};
+ u32 unknown_3{};
+ std::array<char, 0x41> passphrase{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(NifmWirelessSettingData) == 0x70,
+ "NifmWirelessSettingData has incorrect size.");
+
+#pragma pack(push, 1)
+struct SfNetworkProfileData {
+ IpSettingData ip_setting_data{};
+ u128 uuid{};
+ std::array<char, 0x40> network_name{};
+ u8 unknown_1{};
+ u8 unknown_2{};
+ u8 unknown_3{};
+ u8 unknown_4{};
+ SfWirelessSettingData wireless_setting_data{};
+ INSERT_PADDING_BYTES(1);
+};
+static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size.");
+
+struct NifmNetworkProfileData {
+ u128 uuid{};
+ std::array<char, 0x40> network_name{};
+ u32 unknown_1{};
+ u32 unknown_2{};
+ u8 unknown_3{};
+ u8 unknown_4{};
+ INSERT_PADDING_BYTES(2);
+ NifmWirelessSettingData wireless_setting_data{};
+ IpSettingData ip_setting_data{};
+};
+static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
+ "NifmNetworkProfileData has incorrect size.");
+#pragma pack(pop)
+
class IScanRequest final : public ServiceFramework<IScanRequest> {
public:
explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} {
@@ -71,8 +158,11 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- event1 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event1");
- event2 = Kernel::WritableEvent::CreateEventPair(kernel, "IRequest:Event2");
+
+ event1 = Kernel::KEvent::Create(kernel, "IRequest:Event1");
+ event1->Initialize();
+ event2 = Kernel::KEvent::Create(kernel, "IRequest:Event2");
+ event2->Initialize();
}
private:
@@ -108,7 +198,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(event1.readable, event2.readable);
+ rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
}
void Cancel(Kernel::HLERequestContext& ctx) {
@@ -128,14 +218,18 @@ private:
void GetAppletInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 8};
+ std::vector<u8> out_buffer(ctx.GetWriteBufferSize());
+
+ ctx.WriteBuffer(out_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
rb.Push<u32>(0);
rb.Push<u32>(0);
}
- Kernel::EventPair event1, event2;
+ std::shared_ptr<Kernel::KEvent> event1, event2;
};
class INetworkProfile final : public ServiceFramework<INetworkProfile> {
@@ -179,6 +273,46 @@ private:
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRequest>(system);
}
+ void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ const SfNetworkProfileData network_profile_data{
+ .ip_setting_data{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{192, 168, 1, 100},
+ .subnet_mask{255, 255, 255, 0},
+ .gateway{192, 168, 1, 1},
+ },
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
+ },
+ .proxy_setting{
+ .enabled{false},
+ .port{},
+ .proxy_server{},
+ .automatic_auth_enabled{},
+ .user{},
+ .password{},
+ },
+ .mtu{1500},
+ },
+ .uuid{0xdeadbeef, 0xdeadbeef},
+ .network_name{"yuzu Network"},
+ .wireless_setting_data{
+ .ssid_length{12},
+ .ssid{"yuzu Network"},
+ .passphrase{"yuzupassword"},
+ },
+ };
+
+ ctx.WriteBuffer(network_profile_data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -210,6 +344,34 @@ private:
rb.PushIpcInterface<INetworkProfile>(system);
rb.PushRaw<u128>(uuid);
}
+ void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ struct IpConfigInfo {
+ IpAddressSetting ip_address_setting;
+ DnsSetting dns_setting;
+ };
+ static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
+ "IpConfigInfo has incorrect size.");
+
+ const IpConfigInfo ip_config_info{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{192, 168, 1, 100},
+ .subnet_mask{255, 255, 255, 0},
+ .gateway{192, 168, 1, 1},
+ },
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
+ },
+ };
+
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(IpConfigInfo) / sizeof(u32)};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<IpConfigInfo>(ip_config_info);
+ }
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -248,7 +410,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{1, &IGeneralService::GetClientId, "GetClientId"},
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
{4, &IGeneralService::CreateRequest, "CreateRequest"},
- {5, nullptr, "GetCurrentNetworkProfile"},
+ {5, &IGeneralService::GetCurrentNetworkProfile, "GetCurrentNetworkProfile"},
{6, nullptr, "EnumerateNetworkInterfaces"},
{7, nullptr, "EnumerateNetworkProfiles"},
{8, nullptr, "GetNetworkProfile"},
@@ -258,7 +420,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"},
{13, nullptr, "GetCurrentAccessPointOld"},
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
- {15, nullptr, "GetCurrentIpConfigInfo"},
+ {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
{18, nullptr, "GetInternetConnectionStatus"},
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index d16223064..f3be0b878 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -6,9 +6,10 @@
#include <ctime>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -301,17 +302,18 @@ public:
RegisterHandlers(functions);
auto& kernel = system.Kernel();
- finished_event = Kernel::WritableEvent::CreateEventPair(
- kernel, "IEnsureNetworkClockAvailabilityService:FinishEvent");
+ finished_event =
+ Kernel::KEvent::Create(kernel, "IEnsureNetworkClockAvailabilityService:FinishEvent");
+ finished_event->Initialize();
}
private:
- Kernel::EventPair finished_event;
+ std::shared_ptr<Kernel::KEvent> finished_event;
void StartTask(Kernel::HLERequestContext& ctx) {
// No need to connect to the internet, just finish the task straight away.
LOG_DEBUG(Service_NIM, "called");
- finished_event.writable->Signal();
+ finished_event->GetWritableEvent()->Signal();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -321,7 +323,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(finished_event.readable);
+ rb.PushCopyObjects(finished_event->GetReadableEvent());
}
void GetResult(Kernel::HLERequestContext& ctx) {
@@ -333,7 +335,7 @@ private:
void Cancel(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIM, "called");
- finished_event.writable->Clear();
+ finished_event->GetWritableEvent()->Clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 71c7587db..b6ac0a81a 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -65,13 +65,18 @@ static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMem
void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) {
ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
+ if (input.size() < 2) {
+ LOG_ERROR(Service_NS, "Input font is empty");
+ return;
+ }
+
const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
std::vector<u32> transformed_font(input.size());
// TODO(ogniK): Figure out a better way to do this
std::transform(input.begin(), input.end(), transformed_font.begin(),
[&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
- transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size
- std::memcpy(output.data(), transformed_font.data() + 2, transformed_font.size() * sizeof(u32));
+ std::memcpy(output.data(), transformed_font.data() + 2,
+ (transformed_font.size() - 2) * sizeof(u32));
}
void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index fea3b7b9f..f6129ef10 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -8,8 +8,8 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
#include "video_core/gpu.h"
@@ -103,14 +103,14 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
// This is mostly to take into account unimplemented features. As synced
// gpu is always synced.
if (!gpu.IsAsync()) {
- event.event.writable->Signal();
+ event.event->GetWritableEvent()->Signal();
return NvResult::Success;
}
auto lock = gpu.LockSync();
const u32 current_syncpoint_value = event.fence.value;
const s32 diff = current_syncpoint_value - params.threshold;
if (diff >= 0) {
- event.event.writable->Signal();
+ event.event->GetWritableEvent()->Signal();
params.value = current_syncpoint_value;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
@@ -137,7 +137,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
}
params.value |= event_id;
- event.event.writable->Clear();
+ event.event->GetWritableEvent()->Clear();
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Timeout;
@@ -155,7 +155,13 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
return NvResult::BadParameter;
}
if (events_interface.registered[event_id]) {
- return NvResult::BadParameter;
+ const auto event_state = events_interface.status[event_id];
+ if (event_state != EventState::Free) {
+ LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event");
+ events_interface.UnregisterEvent(event_id);
+ } else {
+ return NvResult::BadParameter;
+ }
}
events_interface.RegisterEvent(event_id);
return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index d8735491c..36970f828 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -11,8 +11,9 @@
namespace Service::Nvidia::Devices {
-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(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager)
+ : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {}
nvhost_nvdec::~nvhost_nvdec() = default;
NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 79b8b6de1..77ef53cdd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -11,7 +11,8 @@ namespace Service::Nvidia::Devices {
class nvhost_nvdec final : public nvhost_nvdec_common {
public:
- explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager);
~nvhost_nvdec() override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index b49cecb42..4898dc27a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -11,6 +11,7 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/memory.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
@@ -36,8 +37,9 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
}
} // 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(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager(syncpoint_manager) {}
nvhost_nvdec_common::~nvhost_nvdec_common() = default;
NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
@@ -71,10 +73,15 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
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();
-
+ if (gpu.UseNvdec()) {
+ for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
+ const SyncptIncr& syncpt_incr = syncpt_increments[i];
+ fences[i].id = syncpt_incr.id;
+ fences[i].value =
+ syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments);
+ }
+ }
for (const auto& cmd_buffer : command_buffers) {
auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
@@ -89,7 +96,13 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
cmdlist.size() * sizeof(u32));
gpu.PushCommandBuffer(cmdlist);
}
+ if (gpu.UseNvdec()) {
+ fences[0].value = syncpoint_manager.IncreaseSyncpoint(fences[0].id, 1);
+
+ Tegra::ChCommandHeaderList cmdlist{{(4 << 28) | fences[0].id}};
+ gpu.PushCommandBuffer(cmdlist);
+ }
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// Some games expect command_buffers to be written back
offset = sizeof(IoctlSubmit);
@@ -98,6 +111,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
offset = WriteVectors(output, reloc_shifts, offset);
offset = WriteVectors(output, syncpt_increments, offset);
offset = WriteVectors(output, wait_checks, offset);
+ offset = WriteVectors(output, fences, offset);
return NvResult::Success;
}
@@ -107,9 +121,10 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
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;
+ if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) {
+ device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint();
+ }
+ params.value = device_syncpoints[params.param];
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index d9f95ba58..4c9d4ba41 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -10,12 +10,16 @@
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
-namespace Service::Nvidia::Devices {
+namespace Service::Nvidia {
+class SyncpointManager;
+
+namespace Devices {
class nvmap;
class nvhost_nvdec_common : public nvdevice {
public:
- explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager);
~nvhost_nvdec_common() override;
protected:
@@ -157,8 +161,10 @@ protected:
s32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;
-
+ SyncpointManager& syncpoint_manager;
+ std::array<u32, MaxSyncPoints> device_syncpoints{};
// 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
+}; // namespace Devices
+} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 805fe86ae..72499654c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -10,8 +10,9 @@
#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, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager)
+ : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {}
nvhost_vic::~nvhost_vic() = default;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index b2e11f4d4..f401c61fa 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -7,11 +7,11 @@
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
namespace Service::Nvidia::Devices {
-class nvmap;
class nvhost_vic final : public nvhost_nvdec_common {
public:
- explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager);
~nvhost_vic();
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index cc23b001c..2e1150867 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -6,10 +6,10 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/nvdrv.h"
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 5c777c59b..0e764c53f 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -9,7 +9,7 @@
#include "core/hle/service/service.h"
namespace Kernel {
-class WritableEvent;
+class KWritableEvent;
}
namespace Service::Nvidia {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index e03195afe..abba80112 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -7,8 +7,9 @@
#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"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
@@ -42,7 +43,8 @@ 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::KEvent::Create(kernel, std::move(event_label))};
+ events_interface.events[i].event->Initialize();
events_interface.status[i] = EventState::Free;
events_interface.registered[i] = false;
}
@@ -55,9 +57,11 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
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, syncpoint_manager);
- devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev);
+ devices["/dev/nvhost-nvdec"] =
+ std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager);
devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
- devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev);
+ devices["/dev/nvhost-vic"] =
+ std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager);
}
Module::~Module() = default;
@@ -164,17 +168,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].event.writable->Signal();
+ events_interface.events[i].event->GetWritableEvent()->Signal();
}
}
}
-std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
- return events_interface.events[event_id].event.readable;
+std::shared_ptr<Kernel::KReadableEvent> Module::GetEvent(const u32 event_id) const {
+ return events_interface.events[event_id].event->GetReadableEvent();
}
-std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
- return events_interface.events[event_id].event.writable;
+std::shared_ptr<Kernel::KWritableEvent> Module::GetEventWriteable(const u32 event_id) const {
+ return events_interface.events[event_id].event->GetWritableEvent();
}
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 144e657e5..53719aadd 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -7,8 +7,8 @@
#include <memory>
#include <unordered_map>
#include <vector>
+
#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"
@@ -17,6 +17,10 @@ namespace Core {
class System;
}
+namespace Kernel {
+class KEvent;
+}
+
namespace Service::NVFlinger {
class NVFlinger;
}
@@ -31,7 +35,7 @@ class nvdevice;
/// Represents an Nvidia event
struct NvEvent {
- Kernel::EventPair event;
+ std::shared_ptr<Kernel::KEvent> event;
Fence fence{};
};
@@ -132,9 +136,9 @@ public:
void SignalSyncpt(const u32 syncpoint_id, const u32 value);
- std::shared_ptr<Kernel::ReadableEvent> GetEvent(u32 event_id) const;
+ std::shared_ptr<Kernel::KReadableEvent> GetEvent(u32 event_id) const;
- std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
+ std::shared_ptr<Kernel::KWritableEvent> GetEventWriteable(u32 event_id) const;
private:
/// Manages syncpoints on the host
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 0e6bde9f5..7842a82ed 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -7,16 +7,17 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
namespace Service::NVFlinger {
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id)
: id(id), layer_id(layer_id) {
- buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, "BufferQueue NativeHandle");
+ buffer_wait_event = Kernel::KEvent::Create(kernel, "BufferQueue:WaitEvent");
+ buffer_wait_event->Initialize();
}
BufferQueue::~BufferQueue() = default;
@@ -26,10 +27,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
{
- std::unique_lock lock{queue_mutex};
+ std::unique_lock lock{free_buffers_mutex};
free_buffers.push_back(slot);
}
- condition.notify_one();
+ free_buffers_condition.notify_one();
buffers[slot] = {
.slot = slot,
@@ -41,15 +42,15 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
.multi_fence = {},
};
- buffer_wait_event.writable->Signal();
+ buffer_wait_event->GetWritableEvent()->Signal();
}
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
u32 height) {
// Wait for first request before trying to dequeue
{
- std::unique_lock lock{queue_mutex};
- condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
+ std::unique_lock lock{free_buffers_mutex};
+ free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
}
if (!is_connect) {
@@ -58,7 +59,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
return std::nullopt;
}
- std::unique_lock lock{queue_mutex};
+ std::unique_lock lock{free_buffers_mutex};
auto f_itr = free_buffers.begin();
auto slot = buffers.size();
@@ -100,6 +101,7 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
buffers[slot].crop_rect = crop_rect;
buffers[slot].swap_interval = swap_interval;
buffers[slot].multi_fence = multi_fence;
+ std::unique_lock lock{queue_sequence_mutex};
queue_sequence.push_back(slot);
}
@@ -113,15 +115,16 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
buffers[slot].swap_interval = 0;
{
- std::unique_lock lock{queue_mutex};
+ std::unique_lock lock{free_buffers_mutex};
free_buffers.push_back(slot);
}
- condition.notify_one();
+ free_buffers_condition.notify_one();
- buffer_wait_event.writable->Signal();
+ buffer_wait_event->GetWritableEvent()->Signal();
}
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
+ std::unique_lock lock{queue_sequence_mutex};
std::size_t buffer_slot = buffers.size();
// Iterate to find a queued buffer matching the requested slot.
while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
@@ -147,25 +150,29 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
buffers[slot].status = Buffer::Status::Free;
{
- std::unique_lock lock{queue_mutex};
+ std::unique_lock lock{free_buffers_mutex};
free_buffers.push_back(slot);
}
- condition.notify_one();
+ free_buffers_condition.notify_one();
- buffer_wait_event.writable->Signal();
+ buffer_wait_event->GetWritableEvent()->Signal();
}
void BufferQueue::Connect() {
+ std::unique_lock lock{queue_sequence_mutex};
queue_sequence.clear();
is_connect = true;
}
void BufferQueue::Disconnect() {
buffers.fill({});
- queue_sequence.clear();
- buffer_wait_event.writable->Signal();
+ {
+ std::unique_lock lock{queue_sequence_mutex};
+ queue_sequence.clear();
+ }
+ buffer_wait_event->GetWritableEvent()->Signal();
is_connect = false;
- condition.notify_one();
+ free_buffers_condition.notify_one();
}
u32 BufferQueue::Query(QueryType type) {
@@ -174,18 +181,20 @@ u32 BufferQueue::Query(QueryType type) {
switch (type) {
case QueryType::NativeWindowFormat:
return static_cast<u32>(PixelFormat::RGBA8888);
+ case QueryType::NativeWindowWidth:
+ case QueryType::NativeWindowHeight:
+ break;
}
-
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
return 0;
}
-std::shared_ptr<Kernel::WritableEvent> BufferQueue::GetWritableBufferWaitEvent() const {
- return buffer_wait_event.writable;
+std::shared_ptr<Kernel::KWritableEvent> BufferQueue::GetWritableBufferWaitEvent() const {
+ return buffer_wait_event->GetWritableEvent();
}
-std::shared_ptr<Kernel::ReadableEvent> BufferQueue::GetBufferWaitEvent() const {
- return buffer_wait_event.readable;
+std::shared_ptr<Kernel::KReadableEvent> BufferQueue::GetBufferWaitEvent() const {
+ return buffer_wait_event->GetReadableEvent();
}
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index a2f60d9eb..163fa4c54 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -14,12 +14,14 @@
#include "common/math_util.h"
#include "common/swap.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
namespace Kernel {
class KernelCore;
-}
+class KEvent;
+class KReadableEvent;
+class KWritableEvent;
+} // namespace Kernel
namespace Service::NVFlinger {
@@ -113,9 +115,9 @@ public:
return is_connect;
}
- std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
+ std::shared_ptr<Kernel::KWritableEvent> GetWritableBufferWaitEvent() const;
- std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
+ std::shared_ptr<Kernel::KReadableEvent> GetBufferWaitEvent() const;
private:
BufferQueue(const BufferQueue&) = delete;
@@ -127,10 +129,12 @@ private:
std::list<u32> free_buffers;
std::array<Buffer, buffer_slots> buffers;
std::list<u32> queue_sequence;
- Kernel::EventPair buffer_wait_event;
+ std::shared_ptr<Kernel::KEvent> buffer_wait_event;
- std::mutex queue_mutex;
- std::condition_variable condition;
+ std::mutex free_buffers_mutex;
+ std::condition_variable free_buffers_condition;
+
+ std::mutex queue_sequence_mutex;
};
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 4b3581949..ac2906e5b 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -14,8 +14,8 @@
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hardware_properties.h"
+#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/readable_event.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
@@ -38,6 +38,10 @@ void NVFlinger::SplitVSync() {
system.RegisterHostThread();
std::string name = "yuzu:VSyncThread";
MicroProfileOnThreadCreate(name.c_str());
+
+ // Cleanup
+ SCOPE_EXIT({ MicroProfileOnThreadExit(); });
+
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
s64 delay = 0;
@@ -161,7 +165,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
return layer->GetBufferQueue().GetId();
}
-std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
+std::shared_ptr<Kernel::KReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
const auto guard = Lock();
auto* const display = FindDisplay(display_id);
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index c6765259f..6fe2c7f2a 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -26,8 +26,8 @@ struct EventType;
} // namespace Core::Timing
namespace Kernel {
-class ReadableEvent;
-class WritableEvent;
+class KReadableEvent;
+class KWritableEvent;
} // namespace Kernel
namespace Service::Nvidia {
@@ -72,7 +72,7 @@ public:
/// Gets the vsync event for the specified display.
///
/// If an invalid display ID is provided, then nullptr is returned.
- [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
+ [[nodiscard]] std::shared_ptr<Kernel::KReadableEvent> FindVsyncEvent(u64 display_id) const;
/// Obtains a buffer queue identified by the ID.
[[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp
index 4440135ed..e2ac71fa1 100644
--- a/src/core/hle/service/olsc/olsc.cpp
+++ b/src/core/hle/service/olsc/olsc.cpp
@@ -17,7 +17,7 @@ public:
static const FunctionInfo functions[] = {
{0, &OLSC::Initialize, "Initialize"},
{10, nullptr, "VerifySaveDataBackupLicenseAsync"},
- {13, nullptr, "GetSaveDataBackupSetting"},
+ {13, &OLSC::GetSaveDataBackupSetting, "GetSaveDataBackupSetting"},
{14, &OLSC::SetSaveDataBackupSettingEnabled, "SetSaveDataBackupSettingEnabled"},
{15, nullptr, "SetCustomData"},
{16, nullptr, "DeleteSaveDataBackupSetting"},
@@ -52,6 +52,17 @@ private:
rb.Push(RESULT_SUCCESS);
}
+ void GetSaveDataBackupSetting(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_OLSC, "(STUBBED) called");
+
+ // backup_setting is set to 0 since real value is unknown
+ constexpr u64 backup_setting = 0;
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(backup_setting);
+ }
+
void SetSaveDataBackupSettingEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index 6ab1e4124..f9089bf2f 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -50,11 +50,11 @@ public:
{1046, nullptr, "DisableFeaturesForReset"},
{1047, nullptr, "NotifyApplicationDownloadStarted"},
{1048, nullptr, "NotifyNetworkProfileCreated"},
- {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"},
- {1062, nullptr, "GetStereoVisionRestriction"},
- {1063, nullptr, "SetStereoVisionRestriction"},
- {1064, nullptr, "ResetConfirmedStereoVisionPermission"},
- {1065, nullptr, "IsStereoVisionPermitted"},
+ {1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"},
+ {1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"},
+ {1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"},
+ {1064, &IParentalControlService::ResetConfirmedStereoVisionPermission, "ResetConfirmedStereoVisionPermission"},
+ {1065, &IParentalControlService::IsStereoVisionPermitted, "IsStereoVisionPermitted"},
{1201, nullptr, "UnlockRestrictionTemporarily"},
{1202, nullptr, "UnlockSystemSettingsRestriction"},
{1203, nullptr, "SetPinCode"},
@@ -114,6 +114,7 @@ public:
{2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"},
{2016, nullptr, "RequestUpdateExemptionListAsync"},
};
+ // clang-format on
RegisterHandlers(functions);
}
@@ -131,6 +132,49 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
+ void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PCTL, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PCTL, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(true);
+ }
+
+ void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto can_use = rp.Pop<bool>();
+ LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use);
+
+ can_use_stereo_vision = can_use;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PCTL, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(can_use_stereo_vision);
+ }
+
+ void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PCTL, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ bool can_use_stereo_vision = true;
};
void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
@@ -149,7 +193,8 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext
rb.PushIpcInterface<IParentalControlService>(system);
}
-Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, const char* name)
+Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
+ const char* name)
: ServiceFramework{system_, name}, module{std::move(module_)} {}
Module::Interface::~Interface() = default;
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index b417624c9..d5b3b17a5 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -23,11 +23,11 @@ public:
{10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
{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"},
+ {10104, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
+ {10105, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
+ {10200, &PlayReport::RequestImmediateTransmission, "RequestImmediateTransmission"},
+ {10300, &PlayReport::GetTransmissionStatus, "GetTransmissionStatus"},
+ {10400, &PlayReport::GetSystemSessionId, "GetSystemSessionId"},
{20100, &PlayReport::SaveSystemReport, "SaveSystemReport"},
{20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
{20200, nullptr, "SetOperationMode"},
@@ -59,16 +59,22 @@ private:
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
- std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
- if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
- data.emplace_back(ctx.ReadBuffer(1));
- }
+ const auto data1 = ctx.ReadBuffer(0);
+ const auto data2 = [ctx] {
+ if (ctx.CanReadBuffer(1)) {
+ return ctx.ReadBuffer(1);
+ }
- LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}",
- Type, process_id, data[0].size());
+ return std::vector<u8>{};
+ }();
+
+ LOG_DEBUG(Service_PREPO,
+ "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
+ Type, process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
- reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id);
+ reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
+ process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -79,35 +85,67 @@ private:
IPC::RequestParser rp{ctx};
const auto user_id = rp.PopRaw<u128>();
const auto process_id = rp.PopRaw<u64>();
- std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
- 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));
+ const auto data1 = ctx.ReadBuffer(0);
+ const auto data2 = [ctx] {
+ if (ctx.CanReadBuffer(1)) {
+ return ctx.ReadBuffer(1);
}
- }
- LOG_DEBUG(
- Service_PREPO,
- "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}",
- Type, user_id[1], user_id[0], process_id, data[0].size());
+ return std::vector<u8>{};
+ }();
+
+ LOG_DEBUG(Service_PREPO,
+ "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, "
+ "data1_size={:016X}, data2_size={:016X}",
+ Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
- reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id,
- user_id);
+ reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
+ process_id, user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+ void RequestImmediateTransmission(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PREPO, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetTransmissionStatus(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PREPO, "(STUBBED) called");
+
+ constexpr s32 status = 0;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(status);
+ }
+
+ void GetSystemSessionId(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_PREPO, "(STUBBED) called");
+
+ constexpr u64 system_session_id = 0;
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(system_session_id);
+ }
+
void SaveSystemReport(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
const auto data1 = ctx.ReadBuffer(0);
- const auto data2 = ctx.ReadBuffer(1);
+ const auto data2 = [ctx] {
+ if (ctx.CanReadBuffer(1)) {
+ return ctx.ReadBuffer(1);
+ }
+
+ return std::vector<u8>{};
+ }();
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
title_id, data1.size(), data2.size());
@@ -125,7 +163,13 @@ private:
const auto title_id = rp.PopRaw<u64>();
const auto data1 = ctx.ReadBuffer(0);
- const auto data2 = ctx.ReadBuffer(1);
+ const auto data2 = [ctx] {
+ if (ctx.CanReadBuffer(1)) {
+ return ctx.ReadBuffer(1);
+ }
+
+ return std::vector<u8>{};
+ }();
LOG_DEBUG(Service_PREPO,
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index b4b0dd241..26ed52273 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -5,13 +5,118 @@
#include <memory>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/service/ptm/psm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::PSM {
+class IPsmSession final : public ServiceFramework<IPsmSession> {
+public:
+ explicit IPsmSession(Core::System& system_) : ServiceFramework{system_, "IPsmSession"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IPsmSession::BindStateChangeEvent, "BindStateChangeEvent"},
+ {1, &IPsmSession::UnbindStateChangeEvent, "UnbindStateChangeEvent"},
+ {2, &IPsmSession::SetChargerTypeChangeEventEnabled, "SetChargerTypeChangeEventEnabled"},
+ {3, &IPsmSession::SetPowerSupplyChangeEventEnabled, "SetPowerSupplyChangeEventEnabled"},
+ {4, &IPsmSession::SetBatteryVoltageStateChangeEventEnabled, "SetBatteryVoltageStateChangeEventEnabled"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ state_change_event =
+ Kernel::KEvent::Create(system_.Kernel(), "IPsmSession::state_change_event");
+ state_change_event->Initialize();
+ }
+
+ ~IPsmSession() override = default;
+
+ void SignalChargerTypeChanged() {
+ if (should_signal && should_signal_charger_type) {
+ state_change_event->GetWritableEvent()->Signal();
+ }
+ }
+
+ void SignalPowerSupplyChanged() {
+ if (should_signal && should_signal_power_supply) {
+ state_change_event->GetWritableEvent()->Signal();
+ }
+ }
+
+ void SignalBatteryVoltageStateChanged() {
+ if (should_signal && should_signal_battery_voltage) {
+ state_change_event->GetWritableEvent()->Signal();
+ }
+ }
+
+private:
+ void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PSM, "called");
+
+ should_signal = true;
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(state_change_event->GetReadableEvent());
+ }
+
+ void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PSM, "called");
+
+ should_signal = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto state = rp.Pop<bool>();
+ LOG_DEBUG(Service_PSM, "called, state={}", state);
+
+ should_signal_charger_type = state;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto state = rp.Pop<bool>();
+ LOG_DEBUG(Service_PSM, "called, state={}", state);
+
+ should_signal_power_supply = state;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto state = rp.Pop<bool>();
+ LOG_DEBUG(Service_PSM, "called, state={}", state);
+
+ should_signal_battery_voltage = state;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ bool should_signal_charger_type{};
+ bool should_signal_power_supply{};
+ bool should_signal_battery_voltage{};
+ bool should_signal{};
+ std::shared_ptr<Kernel::KEvent> state_change_event;
+};
+
class PSM final : public ServiceFramework<PSM> {
public:
explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
@@ -24,7 +129,7 @@ public:
{4, nullptr, "IsBatteryChargingEnabled"},
{5, nullptr, "AcquireControllerPowerSupply"},
{6, nullptr, "ReleaseControllerPowerSupply"},
- {7, nullptr, "OpenSession"},
+ {7, &PSM::OpenSession, "OpenSession"},
{8, nullptr, "EnableEnoughPowerChargeEmulation"},
{9, nullptr, "DisableEnoughPowerChargeEmulation"},
{10, nullptr, "EnableFastBatteryCharging"},
@@ -61,6 +166,14 @@ private:
rb.PushEnum(charger_type);
}
+ void OpenSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PSM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IPsmSession>(system);
+ }
+
enum class ChargerType : u32 {
Unplugged = 0,
RegularCharger = 1,
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index ff2a5b1db..1da56bc27 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,10 +11,10 @@
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_port.h"
-#include "core/hle/kernel/thread.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h"
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 4da69f503..2b91a89d1 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -139,9 +139,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
server_port->AppendPendingSession(server);
}
- // Wake the threads waiting on the ServerPort
- server_port->Signal();
-
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 2b824059d..78e9cd708 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -13,7 +13,7 @@
#include "common/microprofile.h"
#include "common/thread.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/network/network.h"
@@ -255,6 +255,25 @@ void BSD::GetSockName(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
+void BSD::GetSockOpt(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+ const u32 level = rp.Pop<u32>();
+ const auto optname = static_cast<OptName>(rp.Pop<u32>());
+
+ LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname);
+
+ std::vector<u8> optval(ctx.GetWriteBufferSize());
+
+ ctx.WriteBuffer(optval);
+
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(-1);
+ rb.PushEnum(Errno::NOTCONN);
+ rb.Push<u32>(static_cast<u32>(optval.size()));
+}
+
void BSD::Listen(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
@@ -401,6 +420,16 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
BuildErrnoResponse(ctx, CloseImpl(fd));
}
+void BSD::EventFd(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 initval = rp.Pop<u64>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
+
+ BuildErrnoResponse(ctx, Errno::SUCCESS);
+}
+
template <typename Work>
void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) {
work.Execute(this);
@@ -424,7 +453,8 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
return {-1, Errno::MFILE};
}
- FileDescriptor& descriptor = file_descriptors[fd].emplace();
+ file_descriptors[fd] = FileDescriptor{};
+ FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={}", fd);
@@ -519,7 +549,8 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
return {-1, Translate(bsd_errno)};
}
- FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace();
+ file_descriptors[new_fd] = FileDescriptor{};
+ FileDescriptor& new_descriptor = *file_descriptors[new_fd];
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
@@ -812,7 +843,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
{14, &BSD::Connect, "Connect"},
{15, &BSD::GetPeerName, "GetPeerName"},
{16, &BSD::GetSockName, "GetSockName"},
- {17, nullptr, "GetSockOpt"},
+ {17, &BSD::GetSockOpt, "GetSockOpt"},
{18, &BSD::Listen, "Listen"},
{19, nullptr, "Ioctl"},
{20, &BSD::Fcntl, "Fcntl"},
@@ -826,7 +857,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
{28, nullptr, "GetResourceStatistics"},
{29, nullptr, "RecvMMsg"},
{30, nullptr, "SendMMsg"},
- {31, nullptr, "EventFd"},
+ {31, &BSD::EventFd, "EventFd"},
{32, nullptr, "RegisterResourceStatisticsName"},
{33, nullptr, "Initialize2"},
};
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 6da0bfeb2..1d2df9c61 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -125,6 +125,7 @@ private:
void Connect(Kernel::HLERequestContext& ctx);
void GetPeerName(Kernel::HLERequestContext& ctx);
void GetSockName(Kernel::HLERequestContext& ctx);
+ void GetSockOpt(Kernel::HLERequestContext& ctx);
void Listen(Kernel::HLERequestContext& ctx);
void Fcntl(Kernel::HLERequestContext& ctx);
void SetSockOpt(Kernel::HLERequestContext& ctx);
@@ -135,6 +136,7 @@ private:
void SendTo(Kernel::HLERequestContext& ctx);
void Write(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
+ void EventFd(Kernel::HLERequestContext& ctx);
template <typename Work>
void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index c822d21b8..ca61d72ca 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -64,6 +64,7 @@ Network::Type Translate(Type type) {
return Network::Type::DGRAM;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
+ return Network::Type{};
}
}
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index 72e1921ec..b78892223 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -73,19 +73,19 @@ struct TimeSpanType {
static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
struct ClockSnapshot {
- SystemClockContext user_context{};
- SystemClockContext network_context{};
- s64 user_time{};
- s64 network_time{};
- TimeZone::CalendarTime user_calendar_time{};
- TimeZone::CalendarTime network_calendar_time{};
- TimeZone::CalendarAdditionalInfo user_calendar_additional_time{};
- TimeZone::CalendarAdditionalInfo network_calendar_additional_time{};
- SteadyClockTimePoint steady_clock_time_point{};
- TimeZone::LocationName location_name{};
- u8 is_automatic_correction_enabled{};
- u8 type{};
- INSERT_PADDING_BYTES(0x2);
+ SystemClockContext user_context;
+ SystemClockContext network_context;
+ s64 user_time;
+ s64 network_time;
+ TimeZone::CalendarTime user_calendar_time;
+ TimeZone::CalendarTime network_calendar_time;
+ TimeZone::CalendarAdditionalInfo user_calendar_additional_time;
+ TimeZone::CalendarAdditionalInfo network_calendar_additional_time;
+ SteadyClockTimePoint steady_clock_time_point;
+ TimeZone::LocationName location_name;
+ u8 is_automatic_correction_enabled;
+ u8 type;
+ INSERT_PADDING_BYTES_NOINIT(0x2);
static ResultCode GetCurrentTime(s64& current_time,
const SteadyClockTimePoint& steady_clock_time_point,
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
index 8af17091c..b9faa474e 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -4,7 +4,7 @@
#include "common/assert.h"
#include "core/core.h"
-#include "core/hle/kernel/writable_event.h"
+#include "core/hle/kernel/k_event.h"
#include "core/hle/service/time/standard_local_system_clock_core.h"
#include "core/hle/service/time/standard_network_system_clock_core.h"
#include "core/hle/service/time/standard_user_system_clock_core.h"
@@ -18,8 +18,10 @@ StandardUserSystemClockCore::StandardUserSystemClockCore(
local_system_clock_core{local_system_clock_core},
network_system_clock_core{network_system_clock_core}, auto_correction_enabled{},
auto_correction_time{SteadyClockTimePoint::GetRandom()},
- auto_correction_event{Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), "StandardUserSystemClockCore:AutoCorrectionEvent")} {}
+ auto_correction_event{Kernel::KEvent::Create(
+ system.Kernel(), "StandardUserSystemClockCore:AutoCorrectionEvent")} {
+ auto_correction_event->Initialize();
+}
ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
bool value) {
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
index ef3d468b7..aac44d72f 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.h
+++ b/src/core/hle/service/time/standard_user_system_clock_core.h
@@ -4,7 +4,6 @@
#pragma once
-#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/time/clock_types.h"
#include "core/hle/service/time/system_clock_core.h"
@@ -12,6 +11,10 @@ namespace Core {
class System;
}
+namespace Kernel {
+class KEvent;
+}
+
namespace Service::Time::Clock {
class StandardLocalSystemClockCore;
@@ -51,7 +54,7 @@ private:
StandardNetworkSystemClockCore& network_system_clock_core;
bool auto_correction_enabled{};
SteadyClockTimePoint auto_correction_time;
- Kernel::EventPair auto_correction_event;
+ std::shared_ptr<Kernel::KEvent> auto_correction_event;
};
} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
index 5cdb80703..bca7d869e 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.cpp
+++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/kernel/writable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/time/errors.h"
#include "core/hle/service/time/system_clock_context_update_callback.h"
@@ -21,7 +21,7 @@ bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& valu
}
void SystemClockContextUpdateCallback::RegisterOperationEvent(
- std::shared_ptr<Kernel::WritableEvent>&& writable_event) {
+ std::shared_ptr<Kernel::KWritableEvent>&& writable_event) {
operation_event_list.emplace_back(std::move(writable_event));
}
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 2b0fa7e75..797954958 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
@@ -9,7 +9,7 @@
#include "core/hle/service/time/clock_types.h"
namespace Kernel {
-class WritableEvent;
+class KWritableEvent;
}
namespace Service::Time::Clock {
@@ -24,7 +24,7 @@ public:
bool NeedUpdate(const SystemClockContext& value) const;
- void RegisterOperationEvent(std::shared_ptr<Kernel::WritableEvent>&& writable_event);
+ void RegisterOperationEvent(std::shared_ptr<Kernel::KWritableEvent>&& writable_event);
void BroadcastOperationEvent();
@@ -37,7 +37,7 @@ protected:
private:
bool has_context{};
- std::vector<std::shared_ptr<Kernel::WritableEvent>> operation_event_list;
+ std::vector<std::shared_ptr<Kernel::KWritableEvent>> operation_event_list;
};
} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index abc753d5d..18629dd7e 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -121,7 +121,7 @@ private:
};
ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
- Kernel::Thread* thread, Clock::SystemClockContext user_context,
+ Kernel::KThread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
auto& time_manager{system.GetTimeManager()};
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 975a8ae5b..4154c7ee9 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -39,7 +39,7 @@ public:
private:
ResultCode GetClockSnapshotFromSystemClockContextInternal(
- Kernel::Thread* thread, Clock::SystemClockContext user_context,
+ Kernel::KThread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, u8 type,
Clock::ClockSnapshot& cloc_snapshot);
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 5976b2046..e0c3e63da 100644
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -6,8 +6,8 @@
#include "common/common_types.h"
#include "common/uuid.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/kernel/thread.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::Time {
diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h
index 9be15b53e..4a57e036d 100644
--- a/src/core/hle/service/time/time_zone_types.h
+++ b/src/core/hle/service/time/time_zone_types.h
@@ -45,23 +45,23 @@ static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
struct CalendarAdditionalInfo {
- u32 day_of_week{};
- u32 day_of_year{};
+ u32 day_of_week;
+ u32 day_of_year;
std::array<char, 8> timezone_name;
- u32 is_dst{};
- s32 gmt_offset{};
+ u32 is_dst;
+ s32 gmt_offset;
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
/// https://switchbrew.org/wiki/Glue_services#CalendarTime
struct CalendarTime {
- s16 year{};
- s8 month{};
- s8 day{};
- s8 hour{};
- s8 minute{};
- s8 second{};
- INSERT_PADDING_BYTES(1);
+ s16 year;
+ s8 month;
+ s8 day;
+ s8 hour;
+ s8 minute;
+ s8 second;
+ INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 5a202ac81..7f42aa4a0 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -9,7 +9,9 @@
#include "common/assert.h"
#include "core/core.h"
-#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
@@ -17,8 +19,8 @@ namespace Service::VI {
Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
auto& kernel = system.Kernel();
- vsync_event =
- Kernel::WritableEvent::CreateEventPair(kernel, fmt::format("Display VSync Event {}", id));
+ vsync_event = Kernel::KEvent::Create(kernel, fmt::format("Display VSync Event {}", id));
+ vsync_event->Initialize();
}
Display::~Display() = default;
@@ -31,12 +33,12 @@ const Layer& Display::GetLayer(std::size_t index) const {
return *layers.at(index);
}
-std::shared_ptr<Kernel::ReadableEvent> Display::GetVSyncEvent() const {
- return vsync_event.readable;
+std::shared_ptr<Kernel::KReadableEvent> Display::GetVSyncEvent() const {
+ return vsync_event->GetReadableEvent();
}
void Display::SignalVSyncEvent() {
- vsync_event.writable->Signal();
+ vsync_event->GetWritableEvent()->Signal();
}
void Display::CreateLayer(u64 id, NVFlinger::BufferQueue& buffer_queue) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index a3855d8cd..931c898f6 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -9,7 +9,10 @@
#include <vector>
#include "common/common_types.h"
-#include "core/hle/kernel/writable_event.h"
+
+namespace Kernel {
+class KEvent;
+}
namespace Service::NVFlinger {
class BufferQueue;
@@ -58,7 +61,7 @@ public:
const Layer& GetLayer(std::size_t index) const;
/// Gets the readable vsync event.
- std::shared_ptr<Kernel::ReadableEvent> GetVSyncEvent() const;
+ std::shared_ptr<Kernel::KReadableEvent> GetVSyncEvent() const;
/// Signals the internal vsync event.
void SignalVSyncEvent();
@@ -99,7 +102,7 @@ private:
std::string name;
std::vector<std::shared_ptr<Layer>> layers;
- Kernel::EventPair vsync_event;
+ std::shared_ptr<Kernel::KEvent> vsync_event;
};
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 968cd16b6..8661895ae 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -18,9 +18,9 @@
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/writable_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"