diff options
261 files changed, 11698 insertions, 6537 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index aaf3a90cf..27aa56780 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,7 +261,7 @@ if(ENABLE_SDL2) find_package(SDL2) if (NOT SDL2_FOUND) # otherwise add this to the list of libraries to install - list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.12@bincrafters/stable") + list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.14@bincrafters/stable") endif() endif() @@ -33,7 +33,7 @@ If you want to contribute to the user interface translation, please check out th ### Support -We happily accept monetary donations or donated games and hardware. Please see our [donations page](https://yuzu-emu.org/donate/) for more information on how you can contribute to yuzu. Any donations received will go towards things like: +We happily accept monetary donations, or donated games and hardware. Please see our [donations page](https://yuzu-emu.org/donate/) for more information on how you can contribute to yuzu. Any donations received will go towards things like: * Switch consoles to explore and reverse-engineer the hardware * Switch games for testing, reverse-engineering, and implementing new features * Web hosting and infrastructure setup diff --git a/dist/icons/controller/controller.qrc b/dist/icons/controller/controller.qrc index 1c4e960c0..78eae461c 100644 --- a/dist/icons/controller/controller.qrc +++ b/dist/icons/controller/controller.qrc @@ -1,26 +1,5 @@ <RCC> <qresource prefix="controller"> - <file alias="dual_joycon">dual_joycon.png</file> - <file alias="dual_joycon_dark">dual_joycon_dark.png</file> - <file alias="dual_joycon_midnight">dual_joycon_midnight.png</file> - <file alias="handheld">handheld.png</file> - <file alias="handheld_dark">handheld_dark.png</file> - <file alias="handheld_midnight">handheld_midnight.png</file> - <file alias="pro_controller">pro_controller.png</file> - <file alias="pro_controller_dark">pro_controller_dark.png</file> - <file alias="pro_controller_midnight">pro_controller_midnight.png</file> - <file alias="single_joycon_left">single_joycon_left.png</file> - <file alias="single_joycon_left_dark">single_joycon_left_dark.png</file> - <file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file> - <file alias="single_joycon_right">single_joycon_right.png</file> - <file alias="single_joycon_right_dark">single_joycon_right_dark.png</file> - <file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file> - <file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file> - <file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file> - <file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file> - <file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file> - <file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file> - <file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file> <file alias="applet_dual_joycon">applet_dual_joycon.png</file> <file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file> <file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file> diff --git a/dist/icons/controller/dual_joycon.png b/dist/icons/controller/dual_joycon.png Binary files differdeleted file mode 100644 index 4230f5f7b..000000000 --- a/dist/icons/controller/dual_joycon.png +++ /dev/null diff --git a/dist/icons/controller/dual_joycon_dark.png b/dist/icons/controller/dual_joycon_dark.png Binary files differdeleted file mode 100644 index 4445db489..000000000 --- a/dist/icons/controller/dual_joycon_dark.png +++ /dev/null diff --git a/dist/icons/controller/dual_joycon_midnight.png b/dist/icons/controller/dual_joycon_midnight.png Binary files differdeleted file mode 100644 index aac8e5321..000000000 --- a/dist/icons/controller/dual_joycon_midnight.png +++ /dev/null diff --git a/dist/icons/controller/handheld.png b/dist/icons/controller/handheld.png Binary files differdeleted file mode 100644 index d009b4a47..000000000 --- a/dist/icons/controller/handheld.png +++ /dev/null diff --git a/dist/icons/controller/handheld_dark.png b/dist/icons/controller/handheld_dark.png Binary files differdeleted file mode 100644 index c80ca9259..000000000 --- a/dist/icons/controller/handheld_dark.png +++ /dev/null diff --git a/dist/icons/controller/handheld_midnight.png b/dist/icons/controller/handheld_midnight.png Binary files differdeleted file mode 100644 index 19de4629b..000000000 --- a/dist/icons/controller/handheld_midnight.png +++ /dev/null diff --git a/dist/icons/controller/pro_controller.png b/dist/icons/controller/pro_controller.png Binary files differdeleted file mode 100644 index 07d65e94a..000000000 --- a/dist/icons/controller/pro_controller.png +++ /dev/null diff --git a/dist/icons/controller/pro_controller_dark.png b/dist/icons/controller/pro_controller_dark.png Binary files differdeleted file mode 100644 index 73efe18f4..000000000 --- a/dist/icons/controller/pro_controller_dark.png +++ /dev/null diff --git a/dist/icons/controller/pro_controller_midnight.png b/dist/icons/controller/pro_controller_midnight.png Binary files differdeleted file mode 100644 index 8d7e63f0d..000000000 --- a/dist/icons/controller/pro_controller_midnight.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_left.png b/dist/icons/controller/single_joycon_left.png Binary files differdeleted file mode 100644 index 547153034..000000000 --- a/dist/icons/controller/single_joycon_left.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_left_dark.png b/dist/icons/controller/single_joycon_left_dark.png Binary files differdeleted file mode 100644 index b6ee073cb..000000000 --- a/dist/icons/controller/single_joycon_left_dark.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_left_midnight.png b/dist/icons/controller/single_joycon_left_midnight.png Binary files differdeleted file mode 100644 index 34a485c81..000000000 --- a/dist/icons/controller/single_joycon_left_midnight.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_left_vertical.png b/dist/icons/controller/single_joycon_left_vertical.png Binary files differdeleted file mode 100644 index 1e6282ad8..000000000 --- a/dist/icons/controller/single_joycon_left_vertical.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_left_vertical_dark.png b/dist/icons/controller/single_joycon_left_vertical_dark.png Binary files differdeleted file mode 100644 index a615d995d..000000000 --- a/dist/icons/controller/single_joycon_left_vertical_dark.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_left_vertical_midnight.png b/dist/icons/controller/single_joycon_left_vertical_midnight.png Binary files differdeleted file mode 100644 index 4cc578216..000000000 --- a/dist/icons/controller/single_joycon_left_vertical_midnight.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_right.png b/dist/icons/controller/single_joycon_right.png Binary files differdeleted file mode 100644 index 8d29173f6..000000000 --- a/dist/icons/controller/single_joycon_right.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_right_dark.png b/dist/icons/controller/single_joycon_right_dark.png Binary files differdeleted file mode 100644 index ead2c44e0..000000000 --- a/dist/icons/controller/single_joycon_right_dark.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_right_midnight.png b/dist/icons/controller/single_joycon_right_midnight.png Binary files differdeleted file mode 100644 index 89afe022d..000000000 --- a/dist/icons/controller/single_joycon_right_midnight.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_right_vertical.png b/dist/icons/controller/single_joycon_right_vertical.png Binary files differdeleted file mode 100644 index 4d7d06547..000000000 --- a/dist/icons/controller/single_joycon_right_vertical.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_right_vertical_dark.png b/dist/icons/controller/single_joycon_right_vertical_dark.png Binary files differdeleted file mode 100644 index 9a6eb3013..000000000 --- a/dist/icons/controller/single_joycon_right_vertical_dark.png +++ /dev/null diff --git a/dist/icons/controller/single_joycon_right_vertical_midnight.png b/dist/icons/controller/single_joycon_right_vertical_midnight.png Binary files differdeleted file mode 100644 index 685249b68..000000000 --- a/dist/icons/controller/single_joycon_right_vertical_midnight.png +++ /dev/null diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 3806284cbefc4115436dcdc687776a45ec31309 +Subproject 8c09da666aa3f0bb1000b0b6c5d5b0a1876f306 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 478246b6f..1cfd3bbc9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,8 +64,10 @@ if (MSVC) else() add_compile_options( -Wall + -Werror=array-bounds -Werror=implicit-fallthrough -Werror=missing-declarations + -Werror=missing-field-initializers -Werror=reorder -Werror=switch -Werror=uninitialized diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index a4a9a757d..5b1065520 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp @@ -383,11 +383,14 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E const auto channel_count = params.channel_count; for (s32 i = 0; i < channel_count; i++) { // TODO(ogniK): Actually implement reverb + /* if (params.input[i] != params.output[i]) { const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); ApplyMix<1>(output, input, 32768, worker_params.sample_count); - } + }*/ + auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); + std::memset(output, 0, worker_params.sample_count * sizeof(s32)); } } diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index f3373fe04..b0f6f0c34 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -51,6 +51,14 @@ void Stream::Stop() { UNIMPLEMENTED(); } +bool Stream::Flush() { + const bool had_buffers = !queued_buffers.empty(); + while (!queued_buffers.empty()) { + queued_buffers.pop(); + } + return had_buffers; +} + void Stream::SetVolume(float volume) { game_volume = volume; } diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 506ac536b..559844b9b 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -56,6 +56,9 @@ public: /// Queues a buffer into the audio stream, returns true on success bool QueueBuffer(BufferPtr&& buffer); + /// Flush audio buffers + bool Flush(); + /// Returns true if the audio stream contains a buffer with the specified tag [[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const; diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h index 863248761..70359cadb 100644 --- a/src/audio_core/voice_context.h +++ b/src/audio_core/voice_context.h @@ -86,28 +86,28 @@ struct BehaviorFlags { static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size"); struct ADPCMContext { - u16 header{}; - s16 yn1{}; - s16 yn2{}; + u16 header; + s16 yn1; + s16 yn2; }; static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size"); struct VoiceState { - s64 played_sample_count{}; - s32 offset{}; - s32 wave_buffer_index{}; - std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{}; - s32 wave_buffer_consumed{}; - std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{}; - s32 fraction{}; - VAddr context_address{}; - Codec::ADPCM_Coeff coeff{}; - ADPCMContext context{}; - std::array<s64, 2> biquad_filter_state{}; - std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{}; - u32 external_context_size{}; - bool is_external_context_used{}; - bool voice_dropped{}; + s64 played_sample_count; + s32 offset; + s32 wave_buffer_index; + std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid; + s32 wave_buffer_consumed; + std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history; + s32 fraction; + VAddr context_address; + Codec::ADPCM_Coeff coeff; + ADPCMContext context; + std::array<s64, 2> biquad_filter_state; + std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples; + u32 external_context_size; + bool is_external_context_used; + bool voice_dropped; }; class VoiceChannelResource { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f77575a00..bfd11e76d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -138,6 +138,8 @@ add_library(common STATIC microprofile.h microprofileui.h misc.cpp + nvidia_flags.cpp + nvidia_flags.h page_table.cpp page_table.h param_package.cpp diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 685e7fc9b..64520ca4e 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -4,13 +4,10 @@ #pragma once +#include <bit> #include <climits> #include <cstddef> -#ifdef _MSC_VER -#include <intrin.h> -#endif - #include "common/common_types.h" namespace Common { @@ -21,48 +18,30 @@ template <typename T> return sizeof(T) * CHAR_BIT; } -#ifdef _MSC_VER - -[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) { - unsigned long result; - _BitScanReverse(&result, value); - return static_cast<u32>(result); -} - -[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) { - unsigned long result; - _BitScanReverse64(&result, value); - return static_cast<u32>(result); -} - -#else - -[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) { - return 31U - static_cast<u32>(__builtin_clz(value)); +[[nodiscard]] constexpr u32 MostSignificantBit32(const u32 value) { + return 31U - static_cast<u32>(std::countl_zero(value)); } -[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) { - return 63U - static_cast<u32>(__builtin_clzll(value)); +[[nodiscard]] constexpr u32 MostSignificantBit64(const u64 value) { + return 63U - static_cast<u32>(std::countl_zero(value)); } -#endif - -[[nodiscard]] inline u32 Log2Floor32(const u32 value) { +[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) { return MostSignificantBit32(value); } -[[nodiscard]] inline u32 Log2Ceil32(const u32 value) { - const u32 log2_f = Log2Floor32(value); - return log2_f + ((value ^ (1U << log2_f)) != 0U); +[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) { + return MostSignificantBit64(value); } -[[nodiscard]] inline u32 Log2Floor64(const u64 value) { - return MostSignificantBit64(value); +[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) { + const u32 log2_f = Log2Floor32(value); + return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U); } -[[nodiscard]] inline u32 Log2Ceil64(const u64 value) { - const u64 log2_f = static_cast<u64>(Log2Floor64(value)); - return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL)); +[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) { + const u64 log2_f = Log2Floor64(value); + return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL)); } } // namespace Common diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 75f3027fb..71b64e32a 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -97,10 +97,27 @@ __declspec(dllimport) void __stdcall DebugBreak(void); #define R_UNLESS(expr, res) \ { \ if (!(expr)) { \ + if (res.IsError()) { \ + LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \ + } \ return res; \ } \ } +#define R_SUCCEEDED(res) (res.IsSuccess()) + +/// Evaluates an expression that returns a result, and returns the result if it would fail. +#define R_TRY(res_expr) \ + { \ + const auto _tmp_r_try_rc = (res_expr); \ + if (_tmp_r_try_rc.IsError()) { \ + return _tmp_r_try_rc; \ + } \ + } + +/// Evaluates a boolean expression, and succeeds if that expression is true. +#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS) + namespace Common { [[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { diff --git a/src/common/nvidia_flags.cpp b/src/common/nvidia_flags.cpp new file mode 100644 index 000000000..d537517db --- /dev/null +++ b/src/common/nvidia_flags.cpp @@ -0,0 +1,27 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <filesystem> +#include <stdlib.h> + +#include <fmt/format.h> + +#include "common/file_util.h" +#include "common/nvidia_flags.h" + +namespace Common { + +void ConfigureNvidiaEnvironmentFlags() { +#ifdef _WIN32 + const std::string shader_path = Common::FS::SanitizePath( + fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir))); + const std::string windows_path = + Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash); + void(Common::FS::CreateFullPath(shader_path + '/')); + void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str())); + void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1")); +#endif +} + +} // namespace Common diff --git a/src/common/nvidia_flags.h b/src/common/nvidia_flags.h new file mode 100644 index 000000000..75a0233ac --- /dev/null +++ b/src/common/nvidia_flags.h @@ -0,0 +1,10 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +namespace Common { + +/// Configure platform specific flags for Nvidia's driver +void ConfigureNvidiaEnvironmentFlags(); + +} // namespace Common diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 138fa0131..4a8d09806 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -19,15 +19,14 @@ namespace Common { /// SPSC ring buffer /// @tparam T Element type /// @tparam capacity Number of slots in ring buffer -/// @tparam granularity Slot size in terms of number of elements -template <typename T, std::size_t capacity, std::size_t granularity = 1> +template <typename T, std::size_t capacity> class RingBuffer { - /// A "slot" is made of `granularity` elements of `T`. - static constexpr std::size_t slot_size = granularity * sizeof(T); + /// A "slot" is made of a single `T`. + static constexpr std::size_t slot_size = sizeof(T); // T must be safely memcpy-able and have a trivial default constructor. static_assert(std::is_trivial_v<T>); // Ensure capacity is sensible. - static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity); + static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2); static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); // Ensure lock-free. static_assert(std::atomic_size_t::is_always_lock_free); @@ -47,7 +46,7 @@ public: const std::size_t second_copy = push_count - first_copy; const char* in = static_cast<const char*>(new_slots); - std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size); + std::memcpy(m_data.data() + pos, in, first_copy * slot_size); in += first_copy * slot_size; std::memcpy(m_data.data(), in, second_copy * slot_size); @@ -74,7 +73,7 @@ public: const std::size_t second_copy = pop_count - first_copy; char* out = static_cast<char*>(output); - std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size); + std::memcpy(out, m_data.data() + pos, first_copy * slot_size); out += first_copy * slot_size; std::memcpy(out, m_data.data(), second_copy * slot_size); @@ -84,9 +83,9 @@ public: } std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) { - std::vector<T> out(std::min(max_slots, capacity) * granularity); - const std::size_t count = Pop(out.data(), out.size() / granularity); - out.resize(count * granularity); + std::vector<T> out(std::min(max_slots, capacity)); + const std::size_t count = Pop(out.data(), out.size()); + out.resize(count); return out; } @@ -113,7 +112,7 @@ private: alignas(128) std::atomic_size_t m_write_index{0}; #endif - std::array<T, granularity * capacity> m_data; + std::array<T, capacity> m_data; }; } // namespace Common diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h index fa46cb394..35dac3a8f 100644 --- a/src/common/scope_exit.h +++ b/src/common/scope_exit.h @@ -49,3 +49,9 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) { * \endcode */ #define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body) + +/** + * This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be + * used when the caller might want to cancel the ScopeExit. + */ +#define SCOPE_GUARD(body) detail::ScopeExit([&]() body) diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 4cba2aaa4..7b614ad89 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -141,27 +141,13 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st } std::string UTF16ToUTF8(const std::u16string& input) { -#ifdef _MSC_VER - // Workaround for missing char16_t/char32_t instantiations in MSVC2017 - std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; - std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend()); - return convert.to_bytes(tmp_buffer); -#else std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; return convert.to_bytes(input); -#endif } std::u16string UTF8ToUTF16(const std::string& input) { -#ifdef _MSC_VER - // Workaround for missing char16_t/char32_t instantiations in MSVC2017 - std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; - auto tmp_buffer = convert.from_bytes(input); - return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend()); -#else std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; return convert.from_bytes(input); -#endif } #ifdef _WIN32 diff --git a/src/common/uuid.h b/src/common/uuid.h index 4ab9a25f0..2e7a18405 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -14,8 +14,8 @@ constexpr u128 INVALID_UUID{{0, 0}}; struct UUID { // UUIDs which are 0 are considered invalid! - u128 uuid = INVALID_UUID; - constexpr UUID() = default; + u128 uuid; + UUID() = default; constexpr explicit UUID(const u128& id) : uuid{id} {} constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 99310dc50..386d7bddf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -160,7 +160,16 @@ add_library(core STATIC hle/kernel/k_affinity_mask.h hle/kernel/k_condition_variable.cpp hle/kernel/k_condition_variable.h + hle/kernel/k_event.cpp + hle/kernel/k_event.h + hle/kernel/k_light_condition_variable.h + hle/kernel/k_light_lock.cpp + hle/kernel/k_light_lock.h hle/kernel/k_priority_queue.h + hle/kernel/k_readable_event.cpp + hle/kernel/k_readable_event.h + hle/kernel/k_resource_limit.cpp + hle/kernel/k_resource_limit.h hle/kernel/k_scheduler.cpp hle/kernel/k_scheduler.h hle/kernel/k_scheduler_lock.h @@ -168,6 +177,11 @@ add_library(core STATIC hle/kernel/k_scoped_scheduler_lock_and_sleep.h hle/kernel/k_synchronization_object.cpp hle/kernel/k_synchronization_object.h + hle/kernel/k_thread.cpp + hle/kernel/k_thread.h + hle/kernel/k_thread_queue.h + hle/kernel/k_writable_event.cpp + hle/kernel/k_writable_event.h hle/kernel/kernel.cpp hle/kernel/kernel.h hle/kernel/memory/address_space_info.cpp @@ -196,10 +210,6 @@ add_library(core STATIC hle/kernel/process.h hle/kernel/process_capability.cpp hle/kernel/process_capability.h - hle/kernel/readable_event.cpp - hle/kernel/readable_event.h - hle/kernel/resource_limit.cpp - hle/kernel/resource_limit.h hle/kernel/server_port.cpp hle/kernel/server_port.h hle/kernel/server_session.cpp @@ -216,14 +226,10 @@ add_library(core STATIC hle/kernel/svc_results.h hle/kernel/svc_types.h hle/kernel/svc_wrap.h - hle/kernel/thread.cpp - hle/kernel/thread.h hle/kernel/time_manager.cpp hle/kernel/time_manager.h hle/kernel/transfer_memory.cpp hle/kernel/transfer_memory.h - hle/kernel/writable_event.cpp - hle/kernel/writable_event.h hle/lock.cpp hle/lock.h hle/result.h @@ -400,8 +406,6 @@ add_library(core STATIC hle/service/ldr/ldr.h hle/service/lm/lm.cpp hle/service/lm/lm.h - hle/service/lm/manager.cpp - hle/service/lm/manager.h hle/service/mig/mig.cpp hle/service/mig/mig.h hle/service/mii/manager.cpp @@ -645,6 +649,7 @@ else() -Werror=implicit-fallthrough -Werror=sign-compare + $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 6c4c8e9e4..ec4407b6e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -71,8 +71,9 @@ public: } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { - LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", - exception, pc, MemoryReadCode(pc)); + LOG_CRITICAL(Core_ARM, + "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", + exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); UNIMPLEMENTED(); } @@ -255,6 +256,9 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { } void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { + if (!jit) { + return; + } Dynarmic::A32::Context context; jit->SaveContext(context); ctx.cpu_registers = context.Regs(); @@ -264,6 +268,9 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { } void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { + if (!jit) { + return; + } Dynarmic::A32::Context context; context.Regs() = ctx.cpu_registers; context.ExtRegs() = ctx.extension_registers; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 35e9ced48..f6c4d4db9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -50,6 +50,10 @@ public: u64 GetTPIDR_EL0() const override; void ChangeProcessorID(std::size_t new_core_id) override; + bool IsInThumbMode() const { + return (GetPSTATE() & 0x20) != 0; + } + void SaveContext(ThreadContext32& ctx) override; void SaveContext(ThreadContext64& ctx) override {} void LoadContext(const ThreadContext32& ctx) override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 4c5ebca22..ae5566ab8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -294,6 +294,9 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { } void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { + if (!jit) { + return; + } ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); ctx.pc = jit->GetPC(); @@ -305,6 +308,9 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { } void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { + if (!jit) { + return; + } jit->SetRegisters(ctx.cpu_registers); jit->SetSP(ctx.sp); jit->SetPC(ctx.pc); diff --git a/src/core/core.cpp b/src/core/core.cpp index 1a2002dec..30f5e1128 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -28,15 +28,14 @@ #include "core/hardware_interrupt_manager.h" #include "core/hle/kernel/client_port.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/physical_core.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/apm/controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/manager.h" -#include "core/hle/service/lm/manager.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/time/time_manager.h" @@ -293,8 +292,6 @@ struct System::Impl { perf_stats->GetMeanFrametime()); } - lm_manager.Flush(); - is_powered_on = false; exit_lock = false; @@ -398,7 +395,6 @@ struct System::Impl { /// Service State Service::Glue::ARPManager arp_manager; - Service::LM::Manager lm_manager{reporter}; Service::Time::TimeManager time_manager; /// Service manager @@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const { return impl->apm_controller; } -Service::LM::Manager& System::GetLogManager() { - return impl->lm_manager; -} - -const Service::LM::Manager& System::GetLogManager() const { - return impl->lm_manager; -} - Service::Time::TimeManager& System::GetTimeManager() { return impl->time_manager; } diff --git a/src/core/core.h b/src/core/core.h index 579a774e4..3a8e040c1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -62,10 +62,6 @@ namespace Glue { class ARPManager; } -namespace LM { -class Manager; -} // namespace LM - namespace SM { class ServiceManager; } // namespace SM @@ -351,9 +347,6 @@ public: [[nodiscard]] Service::APM::Controller& GetAPMController(); [[nodiscard]] const Service::APM::Controller& GetAPMController() const; - [[nodiscard]] Service::LM::Manager& GetLogManager(); - [[nodiscard]] const Service::LM::Manager& GetLogManager() const; - [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 373395047..8f04fb8f5 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -11,9 +11,9 @@ #include "core/core_timing.h" #include "core/cpu_manager.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/physical_core.h" -#include "core/hle/kernel/thread.h" #include "video_core/gpu.h" namespace Core { @@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() { while (true) { auto core = kernel.GetCurrentHostThreadID(); auto& scheduler = *kernel.CurrentScheduler(); - Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + Kernel::KThread* current_thread = scheduler.GetCurrentThread(); Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); ASSERT(scheduler.ContextSwitchPending()); ASSERT(core == kernel.GetCurrentHostThreadID()); @@ -208,7 +208,6 @@ void CpuManager::SingleCoreRunGuestThread() { void CpuManager::SingleCoreRunGuestLoop() { auto& kernel = system.Kernel(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); system.EnterDynarmicProfile(); @@ -217,9 +216,9 @@ void CpuManager::SingleCoreRunGuestLoop() { physical_core = &kernel.CurrentPhysicalCore(); } system.ExitDynarmicProfile(); - thread->SetPhantomMode(true); + kernel.SetIsPhantomModeForSingleCore(true); system.CoreTiming().Advance(); - thread->SetPhantomMode(false); + kernel.SetIsPhantomModeForSingleCore(false); physical_core->ArmInterface().ClearExclusiveState(); PreemptSingleCore(); auto& scheduler = kernel.Scheduler(current_core); @@ -245,7 +244,7 @@ void CpuManager::SingleCoreRunSuspendThread() { while (true) { auto core = kernel.GetCurrentHostThreadID(); auto& scheduler = *kernel.CurrentScheduler(); - Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + Kernel::KThread* current_thread = scheduler.GetCurrentThread(); Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context); ASSERT(scheduler.ContextSwitchPending()); ASSERT(core == kernel.GetCurrentHostThreadID()); @@ -255,22 +254,23 @@ void CpuManager::SingleCoreRunSuspendThread() { void CpuManager::PreemptSingleCore(bool from_running_enviroment) { { - auto& scheduler = system.Kernel().Scheduler(current_core); - Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + auto& kernel = system.Kernel(); + auto& scheduler = kernel.Scheduler(current_core); + Kernel::KThread* current_thread = scheduler.GetCurrentThread(); if (idle_count >= 4 || from_running_enviroment) { if (!from_running_enviroment) { system.CoreTiming().Idle(); idle_count = 0; } - current_thread->SetPhantomMode(true); + kernel.SetIsPhantomModeForSingleCore(true); system.CoreTiming().Advance(); - current_thread->SetPhantomMode(false); + kernel.SetIsPhantomModeForSingleCore(false); } current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); system.CoreTiming().ResetTicks(); scheduler.Unload(scheduler.GetCurrentThread()); - auto& next_scheduler = system.Kernel().Scheduler(current_core); + auto& next_scheduler = kernel.Scheduler(current_core); Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); } @@ -278,8 +278,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { { auto& scheduler = system.Kernel().Scheduler(current_core); scheduler.Reload(scheduler.GetCurrentThread()); - auto* currrent_thread2 = scheduler.GetCurrentThread(); - if (!currrent_thread2->IsIdleThread()) { + if (!scheduler.IsIdle()) { idle_count = 0; } } diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index cebe2ce37..ad116dcc0 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -568,6 +568,11 @@ KeyManager::KeyManager() { // Initialize keys const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath(); const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir); + + if (!Common::FS::Exists(yuzu_keys_dir)) { + Common::FS::CreateDir(yuzu_keys_dir); + } + if (Settings::values.use_dev_keys) { dev_mode = true; AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 17f774baa..86c9f5350 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -58,7 +58,7 @@ struct SaveDataAttribute { SaveDataType type; SaveDataRank rank; u16 index; - INSERT_PADDING_BYTES(4); + INSERT_PADDING_BYTES_NOINIT(4); u64 zero_1; u64 zero_2; u64 zero_3; @@ -72,7 +72,7 @@ struct SaveDataExtraData { u64 owner_id; s64 timestamp; SaveDataFlags flags; - INSERT_PADDING_BYTES(4); + INSERT_PADDING_BYTES_NOINIT(4); s64 available_size; s64 journal_size; s64 commit_id; diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index a287eebe3..a44ce6288 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ } cache.erase(old_path); - file->Open(new_path, "r+b"); - cache.insert_or_assign(new_path, std::move(file)); + if (file->Open(new_path, "r+b")) { + cache.insert_or_assign(new_path, std::move(file)); + } else { + LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); + } } else { UNREACHABLE(); return nullptr; @@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, } auto file = cached.lock(); - file->Open(file_new_path, "r+b"); cache.erase(file_old_path); - cache.insert_or_assign(std::move(file_new_path), std::move(file)); + if (file->Open(file_new_path, "r+b")) { + cache.insert_or_assign(std::move(file_new_path), std::move(file)); + } else { + LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path); + } } return OpenDirectory(new_path, Mode::ReadWrite); diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 8c1193894..ee7a58b1c 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -21,21 +21,18 @@ public: std::mutex mutex; - bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false - - float touch_x = 0.0f; ///< Touchpad X-position - float touch_y = 0.0f; ///< Touchpad Y-position + Input::TouchStatus status; private: class Device : public Input::TouchDevice { public: explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {} - std::tuple<float, float, bool> GetStatus() const override { + Input::TouchStatus GetStatus() const override { if (auto state = touch_state.lock()) { std::lock_guard guard{state->mutex}; - return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed); + return state->status; } - return std::make_tuple(0.0f, 0.0f, false); + return {}; } private: @@ -79,36 +76,44 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi return std::make_tuple(new_x, new_y); } -void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { - if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) +void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { + if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { return; + } + if (id >= touch_state->status.size()) { + return; + } std::lock_guard guard{touch_state->mutex}; - touch_state->touch_x = + const float x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); - touch_state->touch_y = + const float y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); - touch_state->touch_pressed = true; + touch_state->status[id] = std::make_tuple(x, y, true); } -void EmuWindow::TouchReleased() { +void EmuWindow::TouchReleased(std::size_t id) { + if (id >= touch_state->status.size()) { + return; + } std::lock_guard guard{touch_state->mutex}; - touch_state->touch_pressed = false; - touch_state->touch_x = 0; - touch_state->touch_y = 0; + touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false); } -void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { - if (!touch_state->touch_pressed) +void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { + if (id >= touch_state->status.size()) { + return; + } + if (!std::get<2>(touch_state->status[id])) return; if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); - TouchPressed(framebuffer_x, framebuffer_y); + TouchPressed(framebuffer_x, framebuffer_y, id); } void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 276d2b906..2436c6580 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -117,18 +117,23 @@ public: * Signal that a touch pressed event has occurred (e.g. mouse click pressed) * @param framebuffer_x Framebuffer x-coordinate that was pressed * @param framebuffer_y Framebuffer y-coordinate that was pressed + * @param id Touch event ID */ - void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y); + void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); - /// Signal that a touch released event has occurred (e.g. mouse click released) - void TouchReleased(); + /** + * Signal that a touch released event has occurred (e.g. mouse click released) + * @param id Touch event ID + */ + void TouchReleased(std::size_t id); /** * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window) * @param framebuffer_x Framebuffer x-coordinate * @param framebuffer_y Framebuffer y-coordinate + * @param id Touch event ID */ - void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); + void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); /** * Returns currently active configuration. diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index de51a754e..88ebc6497 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -21,6 +21,11 @@ enum class AnalogDirection : u8 { UP, DOWN, }; +struct AnalogProperties { + float deadzone; + float range; + float threshold; +}; /// An abstract class template for an input device (a button, an analog input, etc.). template <typename StatusType> @@ -30,6 +35,12 @@ public: virtual StatusType GetStatus() const { return {}; } + virtual StatusType GetRawStatus() const { + return GetStatus(); + } + virtual AnalogProperties GetAnalogProperties() const { + return {}; + } virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { return {}; } @@ -163,10 +174,11 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common using MotionDevice = InputDevice<MotionStatus>; /** - * A touch status is an object that returns a tuple of two floats and a bool. The floats are - * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed. + * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool. + * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is + * pressed. */ -using TouchStatus = std::tuple<float, float, bool>; +using TouchStatus = std::array<std::tuple<float, float, bool>, 16>; /** * A touch device is an input device that returns a touch status object diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index 456b41e1b..176a72c67 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h @@ -4,8 +4,10 @@ #pragma once +#include <array> #include <tuple> +#include "common/bit_util.h" #include "common/common_types.h" namespace Core { @@ -18,34 +20,12 @@ constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz u constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores -} // namespace Hardware - -constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF; - -struct EmuThreadHandle { - u32 host_handle; - u32 guest_handle; - - u64 GetRaw() const { - return (static_cast<u64>(host_handle) << 32) | guest_handle; - } - - bool operator==(const EmuThreadHandle& rhs) const { - return std::tie(host_handle, guest_handle) == std::tie(rhs.host_handle, rhs.guest_handle); - } - - bool operator!=(const EmuThreadHandle& rhs) const { - return !operator==(rhs); - } - - static constexpr EmuThreadHandle InvalidHandle() { - constexpr u32 invalid_handle = 0xFFFFFFFF; - return {invalid_handle, invalid_handle}; - } - - bool IsInvalid() const { - return (*this) == InvalidHandle(); - } +// Virtual to Physical core map. +constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{ + 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, }; +} // namespace Hardware + } // namespace Core diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 79bcf5762..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"); @@ -174,7 +174,7 @@ struct DomainMessageHeader { 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/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 e8e52900d..a2be1a8f6 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -5,9 +5,9 @@ #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/result.h" namespace Kernel { @@ -38,7 +38,7 @@ 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. diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index d5c9ebee8..85aafeaf4 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -24,7 +24,7 @@ namespace Kernel { class KernelCore; class Session; -class Thread; +class KThread; class ClientSession final : public KSynchronizationObject { public: @@ -46,11 +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 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/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..1a2fa9cd8 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -9,9 +9,9 @@ #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" namespace Kernel { namespace { @@ -89,6 +89,10 @@ ResultCode HandleTable::Close(Handle handle) { 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..7ec62cf18 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -17,16 +17,16 @@ #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/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 index d9e702f13..d0e90fd60 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -7,9 +7,9 @@ #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/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/memory.h" @@ -96,7 +96,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { auto it = thread_tree.nfind_light({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); ASSERT(target_thread->IsWaitingForAddressArbiter()); @@ -118,14 +118,18 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 // Check the userspace value. s32 user_value{}; - R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1), - Svc::ResultInvalidCurrentMemory); - R_UNLESS(user_value == value, Svc::ResultInvalidState); + if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) { + LOG_ERROR(Kernel, "Invalid current memory!"); + return Svc::ResultInvalidCurrentMemory; + } + if (user_value != value) { + return Svc::ResultInvalidState; + } auto it = thread_tree.nfind_light({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); ASSERT(target_thread->IsWaitingForAddressArbiter()); @@ -143,61 +147,34 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 // Perform signaling. s32 num_waiters{}; { - KScopedSchedulerLock sl(kernel); + [[maybe_unused]] const KScopedSchedulerLock sl(kernel); auto it = thread_tree.nfind_light({addr, -1}); // Determine the updated value. s32 new_value{}; - if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) { - if (count <= 0) { - if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - new_value = value - 2; - } else { - new_value = value + 1; - } + if (count <= 0) { + if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) { + new_value = value - 2; } 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; - } + new_value = value + 1; } } else { - if (count <= 0) { - if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { - new_value = value - 1; - } 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) && - (tmp_num_waiters < count + 1)) { - ++tmp_num_waiters; - ++tmp_it; + while (++tmp_it != thread_tree.end() && tmp_it->GetAddressArbiterKey() == addr) { + if (tmp_num_waiters++ >= count) { + break; + } } - if (tmp_num_waiters == 0) { - new_value = value + 1; - } else if (tmp_num_waiters <= count) { + if (tmp_num_waiters < count) { new_value = value - 1; } else { new_value = value; } + } else { + new_value = value + 1; } } @@ -205,17 +182,22 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 s32 user_value{}; bool succeeded{}; if (value != new_value) { - succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value); + succeeded = UpdateIfEqual(system, &user_value, addr, value, new_value); } else { - succeeded = ReadFromUser(system, std::addressof(user_value), addr); + succeeded = ReadFromUser(system, &user_value, addr); } - R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory); - R_UNLESS(user_value == value, Svc::ResultInvalidState); + if (!succeeded) { + LOG_ERROR(Kernel, "Invalid current memory!"); + return Svc::ResultInvalidCurrentMemory; + } + if (user_value != value) { + return Svc::ResultInvalidState; + } while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); ASSERT(target_thread->IsWaitingForAddressArbiter()); @@ -231,11 +213,10 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { // Prepare to wait. - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); { - KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; // Check that the thread isn't terminating. if (cur_thread->IsTerminationRequested()) { @@ -250,9 +231,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement s32 user_value{}; bool succeeded{}; if (decrement) { - succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value); + succeeded = DecrementIfLessThan(system, &user_value, addr, value); } else { - succeeded = ReadFromUser(system, std::addressof(user_value), addr); + succeeded = ReadFromUser(system, &user_value, addr); } if (!succeeded) { @@ -273,17 +254,14 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement } // Set the arbiter. - cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); + 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. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(cur_thread); // Remove from the address arbiter. { @@ -297,16 +275,15 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement // Get the result. KSynchronizationObject* dummy{}; - return cur_thread->GetWaitResult(std::addressof(dummy)); + return cur_thread->GetWaitResult(&dummy); } ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Prepare to wait. - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); { - KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; // Check that the thread isn't terminating. if (cur_thread->IsTerminationRequested()) { @@ -319,7 +296,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Read the value from userspace. s32 user_value{}; - if (!ReadFromUser(system, std::addressof(user_value), addr)) { + if (!ReadFromUser(system, &user_value, addr)) { slp.CancelSleep(); return Svc::ResultInvalidCurrentMemory; } @@ -337,17 +314,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { } // Set the arbiter. - cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); + 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. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(cur_thread); // Remove from the address arbiter. { @@ -361,7 +335,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Get the result. KSynchronizationObject* dummy{}; - return cur_thread->GetWaitResult(std::addressof(dummy)); + return cur_thread->GetWaitResult(&dummy); } } // 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 index 49a068310..f0ad8b390 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -10,11 +10,11 @@ #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/hle/kernel/thread.h" #include "core/memory.h" namespace Kernel { @@ -66,7 +66,7 @@ KConditionVariable::KConditionVariable(Core::System& system_) KConditionVariable::~KConditionVariable() = default; ResultCode KConditionVariable::SignalToAddress(VAddr addr) { - Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); // Signal the address. { @@ -74,7 +74,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { // Remove waiter thread. s32 num_waiters{}; - Thread* next_owner_thread = + KThread* next_owner_thread = owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); // Determine the next tag. @@ -103,11 +103,11 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { } ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); // Wait for the address. { - std::shared_ptr<Thread> owner_thread; + std::shared_ptr<KThread> owner_thread; ASSERT(!owner_thread); { KScopedSchedulerLock sl(kernel); @@ -126,7 +126,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS); // Get the lock owner thread. - owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle); + owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle); R_UNLESS(owner_thread, Svc::ResultInvalidHandle); // Update the lock. @@ -143,7 +143,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val // Remove the thread as a waiter from the lock owner. { KScopedSchedulerLock sl(kernel); - Thread* owner_thread = cur_thread->GetLockOwner(); + KThread* owner_thread = cur_thread->GetLockOwner(); if (owner_thread != nullptr) { owner_thread->RemoveWaiter(cur_thread); } @@ -154,7 +154,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val return cur_thread->GetWaitResult(std::addressof(dummy)); } -Thread* KConditionVariable::SignalImpl(Thread* thread) { +KThread* KConditionVariable::SignalImpl(KThread* thread) { // Check pre-conditions. ASSERT(kernel.GlobalSchedulerContext().IsLocked()); @@ -174,7 +174,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) { } } - Thread* thread_to_close = nullptr; + KThread* thread_to_close = nullptr; if (can_access) { if (prev_tag == InvalidHandle) { // If nobody held the lock previously, we're all good. @@ -182,7 +182,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) { thread->Wakeup(); } else { // Get the previous owner. - auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>( + auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>( prev_tag & ~Svc::HandleWaitMask); if (owner_thread) { @@ -210,8 +210,8 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using // std::shared_ptr. - std::vector<std::shared_ptr<Thread>> thread_list; - std::array<Thread*, MaxThreads> thread_array; + std::vector<std::shared_ptr<KThread>> thread_list; + std::array<KThread*, MaxThreads> thread_array; s32 num_to_close{}; // Perform signaling. @@ -222,9 +222,9 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { auto it = thread_tree.nfind_light({cv_key, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); - if (Thread* thread = SignalImpl(target_thread); thread != nullptr) { + if (KThread* thread = SignalImpl(target_thread); thread != nullptr) { if (num_to_close < MaxThreads) { thread_array[num_to_close++] = thread; } else { @@ -257,11 +257,10 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { // Prepare to wait. - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); { - KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; // Set the synced object. cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); @@ -276,7 +275,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { // Remove waiter thread. s32 num_waiters{}; - Thread* next_owner_thread = + KThread* next_owner_thread = cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); // Update for the next owner thread. @@ -322,16 +321,13 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) } // Cancel the timer wait. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(cur_thread); // Remove from the condition variable. { KScopedSchedulerLock sl(kernel); - if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) { + if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) { owner->RemoveWaiter(cur_thread); } diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 98ed5b323..861dbd420 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h @@ -8,8 +8,8 @@ #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/kernel/thread.h" #include "core/hle/result.h" namespace Core { @@ -20,7 +20,7 @@ namespace Kernel { class KConditionVariable { public: - using ThreadTree = typename Thread::ConditionVariableThreadTreeType; + using ThreadTree = typename KThread::ConditionVariableThreadTreeType; explicit KConditionVariable(Core::System& system_); ~KConditionVariable(); @@ -34,7 +34,7 @@ public: [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); private: - [[nodiscard]] Thread* SignalImpl(Thread* thread); + [[nodiscard]] KThread* SignalImpl(KThread* thread); ThreadTree thread_tree; @@ -43,14 +43,14 @@ private: }; inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, - Thread* thread) { + KThread* thread) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); tree->erase(tree->iterator_to(*thread)); } inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, - Thread* thread) { + KThread* thread) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); tree->insert(*thread); 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 0dc929040..4aa669d95 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -18,17 +18,17 @@ 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); @@ -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..d8a42dbaf --- /dev/null +++ b/src/core/hle/kernel/k_readable_event.cpp @@ -0,0 +1,57 @@ +// 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/errors.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 Svc::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..ab2ab683f --- /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, Svc::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 12b5619fb..bb5f43b53 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -17,25 +17,30 @@ #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) { - const 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) { const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule)); @@ -56,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; } @@ -90,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); } @@ -112,7 +129,7 @@ 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) { const auto core_id = static_cast<u32>(std::countr_zero(idle_cores)); - if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { + if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { s32 migration_candidates[Core::Hardware::NUM_CPU_CORES]; size_t num_candidates = 0; @@ -120,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. @@ -152,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 @@ -182,7 +199,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { return cores_needing_scheduling; } -void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState 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. @@ -205,7 +235,7 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, Thread } } -void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 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. @@ -217,7 +247,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 } } -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()); @@ -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,7 +315,7 @@ 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); } @@ -293,7 +323,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { // 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() >= priority) { - Thread* suggested = priority_queue.GetSuggestedFront(core_id); + 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. @@ -398,7 +428,7 @@ void KScheduler::YieldWithoutCoreMigration() { 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. @@ -442,17 +470,17 @@ void KScheduler::YieldWithCoreMigration() { 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. @@ -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,66 +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) { +void KScheduler::Reload(KThread* thread) { + LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr"); + if (thread) { ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); - // Cancel any outstanding wakeup events for this thread - thread->SetIsRunning(true); - thread->SetWasRunning(false); - 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); @@ -714,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->GetRawState() != ThreadState::Runnable) { - 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 = ¤t_thread->GetHostContext(); + if (next_thread != nullptr) { + next_context = &next_thread->GetHostContext(); } else { next_context = &idle_thread->GetHostContext(); } @@ -744,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) { @@ -764,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->SetState(ThreadState::Runnable); - } } KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel) diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 783665123..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,16 +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, ThreadState 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, s32 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); @@ -114,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: @@ -163,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{}; @@ -178,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{}; }; @@ -186,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 9b40bd22c..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 { @@ -22,46 +23,45 @@ public: 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_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 index 1c508cb55..140cc46a7 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -7,9 +7,9 @@ #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" -#include "core/hle/kernel/thread.h" namespace Kernel { @@ -20,12 +20,11 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, std::vector<ThreadListNode> thread_nodes(num_objects); // Prepare for wait. - Thread* thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* thread = kernel.CurrentScheduler()->GetCurrentThread(); { // Setup the scheduling lock and sleep. - KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, thread, timeout}; // Check if any of the objects are already signaled. for (auto i = 0; i < num_objects; ++i) { @@ -90,10 +89,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, thread->SetWaitObjectsForDebugging({}); // Cancel the timer as needed. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(thread); // Get the wait result. ResultCode wait_result{RESULT_SUCCESS}; @@ -136,7 +132,10 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {} -KSynchronizationObject ::~KSynchronizationObject() = default; +KSynchronizationObject::KSynchronizationObject(KernelCore& kernel, std::string&& name) + : Object{kernel, std::move(name)} {} + +KSynchronizationObject::~KSynchronizationObject() = default; void KSynchronizationObject::NotifyAvailable(ResultCode result) { KScopedSchedulerLock lock(kernel); @@ -148,7 +147,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) { // Iterate over each thread. for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { - Thread* thread = cur_node->thread; + KThread* thread = cur_node->thread; if (thread->GetState() == ThreadState::Waiting) { thread->SetSyncedObject(this, result); thread->SetState(ThreadState::Runnable); @@ -156,8 +155,8 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) { } } -std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { - std::vector<Thread*> threads; +std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { + std::vector<KThread*> threads; // If debugging, dump the list of waiters. { diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h index 14d80ebf1..5803718fd 100644 --- a/src/core/hle/kernel/k_synchronization_object.h +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -13,14 +13,14 @@ namespace Kernel { class KernelCore; class Synchronization; -class Thread; +class KThread; /// Class that represents a Kernel object that a thread can be waiting on class KSynchronizationObject : public Object { public: struct ThreadListNode { ThreadListNode* next{}; - Thread* thread{}; + KThread* thread{}; }; [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, @@ -29,10 +29,11 @@ public: [[nodiscard]] virtual bool IsSignaled() const = 0; - [[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const; + [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const; protected: explicit KSynchronizationObject(KernelCore& kernel); + explicit KSynchronizationObject(KernelCore& kernel, std::string&& name); virtual ~KSynchronizationObject(); void NotifyAvailable(ResultCode result); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp new file mode 100644 index 000000000..b59259c4f --- /dev/null +++ b/src/core/hle/kernel/k_thread.cpp @@ -0,0 +1,1050 @@ +// 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/errors.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 = Svc::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, Svc::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, Svc::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(), + Svc::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, Svc::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), + Svc::ResultInvalidState); + + // Either pause or resume. + if (activity == Svc::ThreadActivity::Paused) { + // Verify that we're not suspended. + R_UNLESS(!IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState); + + // Suspend. + RequestSuspend(SuspendType::Thread); + } else { + ASSERT(activity == Svc::ThreadActivity::Runnable); + + // Verify that we're suspended. + R_UNLESS(IsSuspendRequested(SuspendType::Thread), Svc::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(), + Svc::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), Svc::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(), Svc::ResultTerminationRequested); + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), + Svc::ResultTerminationRequested); + + // Ensure our thread state is correct. + R_UNLESS(GetState() == ThreadState::Initialized, Svc::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 Svc::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 c0ff287a6..b20c2d13a 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -28,17 +28,17 @@ #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/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/lock.h" #include "core/hle/result.h" @@ -57,14 +57,16 @@ struct KernelCore::Impl { } 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(); @@ -116,32 +118,32 @@ 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(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::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(LimitableResource::Sessions, 900).IsSuccess()); - if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) || - !system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) { + if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 0x60000)) { UNREACHABLE(); } } @@ -168,11 +170,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(); } @@ -207,6 +207,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); @@ -219,6 +230,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() { @@ -229,20 +241,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() { @@ -307,7 +320,7 @@ struct KernelCore::Impl { std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 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; @@ -342,11 +355,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{}; @@ -376,12 +390,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) { @@ -546,8 +560,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() { @@ -645,4 +659,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 933d9a7d6..806a0d986 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -38,14 +38,18 @@ 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: @@ -81,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); @@ -161,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; @@ -237,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/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index 080886554..7de91c768 100644 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp @@ -7,6 +7,7 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/k_resource_limit.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 +16,6 @@ #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/memory/system_control.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" #include "core/memory.h" namespace Kernel::Memory { @@ -414,7 +414,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { const std::size_t remaining_pages{remaining_size / PageSize}; if (process->GetResourceLimit() && - !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, remaining_size)) { + !process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, remaining_size)) { return ERR_RESOURCE_LIMIT_EXCEEDED; } @@ -422,7 +422,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { { auto block_guard = detail::ScopeExit([&] { system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool); - process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, remaining_size); + process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, remaining_size); }); CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, @@ -474,7 +474,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; @@ -783,7 +783,7 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) { auto process{system.Kernel().CurrentProcess()}; if (process->GetResourceLimit() && delta != 0 && - !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, delta)) { + !process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, delta)) { return ERR_RESOURCE_LIMIT_EXCEEDED; } 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 27124ef67..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,7 +48,7 @@ public: return "[BAD KERNEL OBJECT TYPE]"; } virtual std::string GetName() const { - return "[UNKNOWN KERNEL OBJECT]"; + return name; } virtual HandleType GetHandleType() const = 0; @@ -61,12 +63,15 @@ public: */ 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 37b77fa6e..2286b292d 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -15,14 +15,15 @@ #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_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,10 @@ 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); + 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(); @@ -117,7 +117,7 @@ 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); + 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 +133,29 @@ 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) + + const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; @@ -162,26 +179,79 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); } -void Process::RegisterThread(const Thread* thread) { +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); + } + } + + return true; + } else { + return false; + } +} + +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); +} + +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 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, Svc::ResultInvalidState); + R_UNLESS(is_signaled, Svc::ResultInvalidState); + // Clear signaled. is_signaled = false; return RESULT_SUCCESS; } @@ -237,13 +307,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, // Set initial resource limits resource_limit->SetLimitValue( - ResourceType::PhysicalMemory, + LimitableResource::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); + ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemory, code_size)); // Create TLS region tls_region_address = CreateTLSRegion(); @@ -260,14 +330,14 @@ 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); + resource_limit->Reserve(LimitableResource::Threads, 1); + resource_limit->Reserve(LimitableResource::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; @@ -279,7 +349,7 @@ void Process::PrepareForTermination() { ASSERT_MSG(thread->GetState() == ThreadState::Waiting, "Exiting processes with non-waiting threads is currently unimplemented"); - thread->Stop(); + thread->Exit(); } }; @@ -372,7 +442,7 @@ bool Process::IsSignaled() const { Process::Process(Core::System& system) : KSynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()}, - address_arbiter{system}, condition_var{system}, system{system} {} + address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {} Process::~Process() = default; diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 564e1f27d..320b0f347 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -29,8 +29,8 @@ class ProgramMetadata; namespace Kernel { class KernelCore; -class ResourceLimit; -class Thread; +class KResourceLimit; +class KThread; class TLSPage; struct CodeSet; @@ -170,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(); @@ -212,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; @@ -232,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); @@ -252,17 +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; } /// 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. /// @@ -272,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 @@ -303,6 +343,15 @@ public: bool IsSignaled() const override; + void Finalize() override {} + + void PinCurrentThread(); + void UnpinCurrentThread(); + + KLightLock& GetStateLock() { + return state_lock; + } + /////////////////////////////////////////////////////////////////////////////////////////////// // Thread-local storage management @@ -313,6 +362,20 @@ public: void FreeTLSRegion(VAddr tls_address); private: + 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; + } + + 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 /// a process signal. @@ -339,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; @@ -380,7 +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; + std::list<const KThread*> thread_list; /// Address of the top of the main thread's stack VAddr main_thread_stack_top{}; @@ -401,6 +464,19 @@ private: 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/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp deleted file mode 100644 index 99ed0857e..000000000 --- a/src/core/hle/kernel/readable_event.cpp +++ /dev/null @@ -1,52 +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) : KSynchronizationObject{kernel} {} -ReadableEvent::~ReadableEvent() = default; - -void ReadableEvent::Signal() { - if (is_signaled) { - return; - } - - is_signaled = true; - NotifyAvailable(); -} - -bool ReadableEvent::IsSignaled() const { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - - return is_signaled; -} - -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 34e477274..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/k_synchronization_object.h" -#include "core/hle/kernel/object.h" - -union ResultCode; - -namespace Kernel { - -class KernelCore; -class WritableEvent; - -class ReadableEvent final : public KSynchronizationObject { - 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; - } - - /// 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(); - - bool IsSignaled() const override; - -private: - explicit ReadableEvent(KernelCore& kernel); - - bool is_signaled{}; - 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 82857f93b..fe7a483c4 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -6,10 +6,10 @@ #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" namespace Kernel { diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6470df993..29b4f2509 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -81,6 +81,8 @@ public: 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 4f2bb7822..790dbb998 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -15,11 +15,11 @@ #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 { @@ -116,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 = @@ -154,14 +154,14 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) { KScopedSchedulerLock lock(kernel); if (!context.IsThreadWaiting()) { context.GetThread().Wakeup(); - context.GetThread().SetSynchronizationResults(nullptr, result); + 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 9155cf7f5..c42d5ee59 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -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 @@ -95,7 +95,7 @@ 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); /// Called when a client disconnection occurs. @@ -126,9 +126,11 @@ public: 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); @@ -149,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.h b/src/core/hle/kernel/session.h index f6dd2c1d2..fa3c5651a 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -39,6 +39,8 @@ public: bool IsSignaled() const override; + void Finalize() override {} + std::shared_ptr<ClientSession> Client() { if (auto result{client.lock()}) { return result; 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 cc8b661af..26650a513 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -14,6 +14,7 @@ #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" @@ -26,26 +27,27 @@ #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_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/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/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" @@ -141,7 +143,7 @@ enum class ResourceLimitValueType { ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, u32 resource_type, ResourceLimitValueType value_type) { std::lock_guard lock{HLE::g_hle_lock}; - const auto type = static_cast<ResourceType>(resource_type); + 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; @@ -151,7 +153,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_ 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); @@ -159,10 +161,10 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_ } if (value_type == ResourceLimitValueType::CurrentValue) { - return MakeResult(resource_limit_object->GetCurrentResourceValue(type)); + return MakeResult(resource_limit_object->GetCurrentValue(type)); } - return MakeResult(resource_limit_object->GetMaxResourceValue(type)); + return MakeResult(resource_limit_object->GetLimitValue(type)); } } // Anonymous namespace @@ -312,7 +314,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, return ERR_NOT_FOUND; } - ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1)); + ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(LimitableResource::Sessions, 1)); auto client_port = it->second; @@ -351,7 +353,8 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); } - return thread->GetSignalingResult(); + KSynchronizationObject* dummy{}; + return thread->GetWaitResult(std::addressof(dummy)); } static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { @@ -359,27 +362,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; } @@ -395,7 +400,7 @@ 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) { @@ -473,15 +478,16 @@ 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; } @@ -496,8 +502,15 @@ static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAdd thread_handle, address, tag); // Validate the input address. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); + if (Memory::IsKernelAddress(address)) { + LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock 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; + } return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); } @@ -512,8 +525,16 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); // Validate the input address. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); + 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; + } return system.Kernel().CurrentProcess()->SignalToAddress(address); } @@ -630,7 +651,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(); } } @@ -872,7 +893,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha return ERR_INVALID_COMBINATION; } - 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}", @@ -888,7 +909,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()) { @@ -1026,128 +1047,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; } + // Set the thread priority. thread->SetBasePriority(priority); - return RESULT_SUCCESS; } @@ -1438,62 +1470,62 @@ 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); + "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); - auto* const current_process = system.Kernel().CurrentProcess(); - - 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)); + ASSERT(process.GetResourceLimit()->Reserve( + LimitableResource::Threads, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000)); - 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); @@ -1517,17 +1549,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->GetState() == ThreadState::Initialized); + // 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) { @@ -1540,7 +1579,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) { @@ -1549,34 +1588,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); } } @@ -1592,8 +1625,14 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, cv_key, tag, timeout_ns); // Validate input. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); + 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{}; @@ -1668,9 +1707,18 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::Arbit address, arb_type, value, timeout_ns); // Validate input. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); - R_UNLESS(IsValidArbitrationType(arb_type), Svc::ResultInvalidEnumValue); + 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{}; @@ -1704,9 +1752,18 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign address, signal_type, value, count); // Validate input. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(s32)), Svc::ResultInvalidAddress); - R_UNLESS(IsValidSignalType(signal_type), Svc::ResultInvalidEnumValue); + if (Memory::IsKernelAddress(address)) { + LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address); + return ResultInvalidCurrentMemory; + } + 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; + } return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, count); @@ -1766,20 +1823,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 Svc::ResultInvalidHandle; } static ResultCode ResetSignal32(Core::System& system, Handle handle) { @@ -1839,171 +1904,193 @@ 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; + } + + // 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 thread->SetCoreAndAffinityMask(core, affinity_mask); + 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"); - - auto& kernel = system.Kernel(); - const auto [readable_event, writable_event] = - WritableEvent::CreateEventPair(kernel, "CreateEvent"); +static ResultCode SignalEvent(Core::System& system, Handle event_handle) { + LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); - HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable(); + // Get the current handle table. + const HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - const auto write_create_result = handle_table.Create(writable_event); - if (write_create_result.Failed()) { - return write_create_result.Code(); - } - *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; + 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 Svc::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"); + + // Get the kernel reference and handle table. + auto& kernel = system.Kernel(); + HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable(); - HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - auto writable_event = handle_table.Get<WritableEvent>(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; + } - if (!writable_event) { - LOG_ERROR(Kernel_SVC, "Non-existent writable event handle used (0x{:08X})", handle); - return ERR_INVALID_HANDLE; + // 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; - writable_event->Signal(); + // 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) { @@ -2037,7 +2124,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); @@ -2084,7 +2171,7 @@ 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; @@ -2094,7 +2181,7 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour 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); @@ -2106,8 +2193,8 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour 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)); + resource_limit_object->GetLimitValue(type), resource_type, + resource_limit_object->GetCurrentValue(type)); return set_result; } @@ -2491,7 +2578,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); @@ -2507,7 +2594,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_results.h b/src/core/hle/kernel/svc_results.h index 78282f021..204cd989d 100644 --- a/src/core/hle/kernel/svc_results.h +++ b/src/core/hle/kernel/svc_results.h @@ -8,13 +8,19 @@ namespace Kernel::Svc { +constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57}; constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; +constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103}; constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; +constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112}; +constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113}; constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114}; +constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116}; constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; +constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122}; constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index d623f7a50..ec463b97c 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -77,4 +77,22 @@ enum class ArbitrationType : u32 { 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 a32750ed7..96afd544b 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -58,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), @@ -158,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)), ¶m_1, ¶m_2); @@ -473,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), ¶m_1, ¶m_2, ¶m_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) { @@ -487,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 = @@ -497,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) { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp deleted file mode 100644 index d97323255..000000000 --- a/src/core/hle/kernel/thread.cpp +++ /dev/null @@ -1,460 +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_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/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/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::IsSignaled() const { - return signaled; -} - -Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {} -Thread::~Thread() = default; - -void Thread::Stop() { - { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Terminated); - signaled = true; - NotifyAvailable(); - 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::Wakeup() { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); -} - -ResultCode Thread::Start() { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); - return RESULT_SUCCESS; -} - -void Thread::CancelWait() { - KScopedSchedulerLock lock(kernel); - if (GetState() != ThreadState::Waiting || !is_cancellable) { - is_sync_cancelled = true; - return; - } - // TODO(Blinkhawk): Implement cancel of server session - is_sync_cancelled = false; - SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); - SetState(ThreadState::Runnable); -} - -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->thread_state = ThreadState::Initialized; - thread->entry_point = entry_point; - thread->stack_top = stack_top; - thread->disable_count = 1; - thread->tpidr_el0 = 0; - thread->current_priority = priority; - thread->base_priority = priority; - thread->lock_owner = nullptr; - 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->name = std::move(name); - thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); - thread->owner_process = owner_process; - thread->type = type_flags; - thread->signaled = false; - 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::SetBasePriority(u32 priority) { - ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, - "Invalid priority value."); - - KScopedSchedulerLock lock(kernel); - - // Change our base priority. - base_priority = priority; - - // Perform a priority restoration. - RestorePriority(kernel, this); -} - -void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) { - signaling_object = object; - signaling_result = result; -} - -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::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); - } -} - -void Thread::AddWaiterImpl(Thread* 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 Thread::RemoveWaiterImpl(Thread* 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 Thread::RestorePriority(KernelCore& kernel, Thread* 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. - Thread* 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 Thread::AddWaiter(Thread* thread) { - AddWaiterImpl(thread); - RestorePriority(kernel, this); -} - -void Thread::RemoveWaiter(Thread* thread) { - RemoveWaiterImpl(thread); - RestorePriority(kernel, this); -} - -Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - - s32 num_waiters{}; - Thread* next_lock_owner{}; - auto it = waiter_list.begin(); - while (it != waiter_list.end()) { - if (it->GetAddressKey() == key) { - Thread* 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 Thread::SetActivity(ThreadActivity value) { - KScopedSchedulerLock lock(kernel); - - auto sched_status = GetState(); - - if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) { - return ERR_INVALID_STATE; - } - - if (IsTerminationRequested()) { - 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); - SetState(ThreadState::Waiting); - SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); - } - - if (event_handle != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(event_handle); - } - return RESULT_SUCCESS; -} - -void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); - pausing_state |= static_cast<u32>(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); - KScheduler::OnThreadStateChanged(kernel, this, old_state); -} - -void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); - pausing_state &= ~static_cast<u32>(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); - KScheduler::OnThreadStateChanged(kernel, this, old_state); -} - -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 6b66c9a0e..000000000 --- a/src/core/hle/kernel/thread.h +++ /dev/null @@ -1,782 +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 <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_synchronization_object.h" -#include "core/hle/kernel/object.h" -#include "core/hle/kernel/svc_common.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 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 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 ThreadSchedFlags : u32 { - ProcessPauseFlag = 1 << 4, - ThreadPauseFlag = 1 << 5, - ProcessDebugPauseFlag = 1 << 6, - KernelInitPauseFlag = 1 << 8, -}; - -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 -}; - -class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { - friend class KScheduler; - friend class Process; - -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; - - /** - * 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; - } - - /** - * Gets the thread's current priority - * @return The current thread's priority - */ - [[nodiscard]] s32 GetPriority() const { - return current_priority; - } - - /** - * Sets the thread's current priority. - * @param priority The new priority. - */ - void SetPriority(s32 priority) { - current_priority = priority; - } - - /** - * Gets the thread's nominal priority. - * @return The current thread's nominal priority. - */ - [[nodiscard]] s32 GetBasePriority() const { - return base_priority; - } - - /** - * Sets the thread's nominal priority. - * @param priority The new priority. - */ - void SetBasePriority(u32 priority); - - /// Changes the core that the thread is running or scheduled to run on. - [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); - - /** - * Gets the thread's thread ID - * @return The thread's ID - */ - [[nodiscard]] u64 GetThreadID() const { - return thread_id; - } - - /// Resumes a thread from waiting - void Wakeup(); - - ResultCode Start(); - - virtual bool IsSignaled() const override; - - /// 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(KSynchronizationObject* object, ResultCode result); - - void SetSyncedObject(KSynchronizationObject* object, ResultCode result) { - SetSynchronizationResults(object, result); - } - - ResultCode GetWaitResult(KSynchronizationObject** out) const { - *out = signaling_object; - return signaling_result; - } - - ResultCode GetSignalingResult() const { - return signaling_result; - } - - /** - * 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(); - - ThreadState GetState() const { - return thread_state & ThreadState::Mask; - } - - ThreadState GetRawState() const { - return thread_state; - } - - void SetState(ThreadState state); - - s64 GetLastScheduledTick() const { - return last_scheduled_tick; - } - - void SetLastScheduledTick(s64 tick) { - 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 MutexWaitingThreads& GetMutexWaitingThreads() const { - return wait_mutex_threads; - } - - Thread* GetLockOwner() const { - return lock_owner; - } - - void SetLockOwner(Thread* owner) { - lock_owner = owner; - } - - 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 schedule_count; - } - - void SetYieldScheduleCount(s64 count) { - schedule_count = count; - } - - bool IsRunning() const { - return is_running; - } - - void SetIsRunning(bool value) { - is_running = value; - } - - bool IsWaitCancelled() const { - return is_sync_cancelled; - } - - void ClearWaitCancelled() { - is_sync_cancelled = false; - } - - Handle GetGlobalHandle() const { - return global_handle; - } - - bool IsCancellable() const { - return is_cancellable; - } - - void SetCancellable() { - is_cancellable = true; - } - - void ClearCancellable() { - is_cancellable = false; - } - - bool IsTerminationRequested() const { - return will_be_terminated || GetRawState() == ThreadState::Terminated; - } - - 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() { - prev = nullptr; - next = nullptr; - } - - constexpr Thread* GetPrev() const { - return prev; - } - constexpr Thread* GetNext() const { - return next; - } - constexpr void SetPrev(Thread* thread) { - prev = thread; - } - constexpr void SetNext(Thread* thread) { - next = thread; - } - - private: - Thread* prev{}; - Thread* next{}; - }; - - QueueEntry& GetPriorityQueueEntry(s32 core) { - return per_core_priority_queue_entry[core]; - } - - const QueueEntry& GetPriorityQueueEntry(s32 core) const { - return 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--; - } - - void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) { - wait_reason_for_debugging = reason; - } - - [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const { - return wait_reason_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; - } - - void AddWaiter(Thread* thread); - - void RemoveWaiter(Thread* thread); - - [[nodiscard]] Thread* 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; - } - -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, Thread> || - std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, - const Thread& rhs) { - const uintptr_t l_key = lhs.GetConditionVariableKey(); - const uintptr_t 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; - } - } - }; - - Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; - - using ConditionVariableThreadTreeTraits = - Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>; - using ConditionVariableThreadTree = - ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>; - -public: - using ConditionVariableThreadTreeType = ConditionVariableThreadTree; - - [[nodiscard]] uintptr_t GetConditionVariableKey() const { - return condvar_key; - } - - [[nodiscard]] uintptr_t GetAddressArbiterKey() const { - return condvar_key; - } - - void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t 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, uintptr_t 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; - } - - [[nodiscard]] bool HasWaiters() const { - return !waiter_list.empty(); - } - -private: - void AddSchedulingFlag(ThreadSchedFlags flag); - void RemoveSchedulingFlag(ThreadSchedFlags flag); - void AddWaiterImpl(Thread* thread); - void RemoveWaiterImpl(Thread* thread); - static void RestorePriority(KernelCore& kernel, Thread* thread); - - Common::SpinLock context_guard{}; - ThreadContext32 context_32{}; - ThreadContext64 context_64{}; - std::shared_ptr<Common::Fiber> host_context{}; - - ThreadState thread_state = ThreadState::Initialized; - - 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. - s32 base_priority{}; - - /// Current thread priority. This may change over the course of the - /// thread's lifetime in order to facilitate priority inheritance. - s32 current_priority{}; - - 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. This is used for debugging only. - std::vector<KSynchronizationObject*> wait_objects_for_debugging; - - /// The current mutex wait address. This is used for debugging only. - VAddr mutex_wait_address_for_debugging{}; - - /// The reason the thread is waiting. This is used for debugging only. - ThreadWaitReasonForDebugging wait_reason_for_debugging{}; - - KSynchronizationObject* 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. - Thread* lock_owner{}; - - /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. - Handle global_handle = 0; - - 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_cancellable = 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; - - bool signaled{}; - - ConditionVariableThreadTree* condvar_tree{}; - uintptr_t condvar_key{}; - VAddr address_key{}; - u32 address_key_value{}; - s32 num_kernel_waiters{}; - - using WaiterList = boost::intrusive::list<Thread>; - WaiterList waiter_list{}; - WaiterList pinned_waiter_list{}; - - std::string name; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 832edd629..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,50 +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) { - std::shared_ptr<Thread> thread; + std::shared_ptr<KThread> thread; { std::lock_guard lock{mutex}; - const auto proper_handle = static_cast<Handle>(thread_handle); - if (cancelled_events[proper_handle]) { - return; - } - thread = system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); - } - - if (thread) { - // Thread can be null if process has exited - thread->Wakeup(); + 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->GetState() != ThreadState::Runnable); + 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.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..bb77c2569 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"); @@ -1178,7 +1195,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 +1216,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 +1232,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 +1487,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 +1637,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 +1645,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 +1697,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 +1716,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..d7d3ee99a 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, 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/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..dbf198345 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -12,9 +12,10 @@ #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" @@ -141,7 +142,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 +154,86 @@ 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::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 +241,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 +258,8 @@ 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.palma.Assign(1); } std::transform(Settings::values.players.GetValue().begin(), @@ -402,13 +413,10 @@ 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}; for (auto* main_controller : controller_npads) { main_controller->common.entry_count = 16; @@ -438,19 +446,19 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* auto& pad_state = npad_pad_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]; 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 +466,67 @@ 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::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 +560,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 +598,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 +615,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 +625,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 +635,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 +647,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 +655,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,7 +665,9 @@ 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; @@ -713,8 +733,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 +752,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 +890,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 +911,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 +942,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 +973,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,7 +1128,7 @@ 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: @@ -1090,7 +1136,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const case NPadControllerType::JoyRight: return style.joycon_right; 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..48bab988c 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; @@ -90,17 +94,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 +112,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 +196,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 +211,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 +247,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 +314,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 +330,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 +361,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 +378,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 +389,54 @@ private: }; static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size"); - enum class ColorReadError : u32_le { - ReadOk = 0, - ColorDoesntExist = 1, - NoController = 2, - }; - - struct NPadProperties { + 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 +447,69 @@ 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); + }; - ColorReadError dual_color_error; - ControllerColor left_color; - ControllerColor right_color; + 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, + }; + + 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(0xdef); }; static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); @@ -439,10 +545,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,6 +556,8 @@ 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{}; bool is_in_lr_assignment_mode{false}; 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 d86711fea..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( @@ -219,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"}, @@ -411,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>()}; @@ -441,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>()}; @@ -462,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>()}; @@ -483,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>()}; @@ -504,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>()}; @@ -525,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>()}; @@ -544,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>()}; @@ -566,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>()}; @@ -587,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>()}; @@ -609,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>()}; @@ -630,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>()}; @@ -712,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>()}; @@ -732,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>()}; @@ -766,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>()}; @@ -810,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>()}; @@ -831,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>()}; @@ -854,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>()}; @@ -962,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>()}; @@ -981,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>()}; @@ -1036,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>()}; @@ -1060,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>()}; @@ -1157,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>()}; @@ -1190,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>()}; @@ -1210,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/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 641bcadea..5d6d25696 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: @@ -262,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) { @@ -316,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; }; @@ -336,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(), ¶ms, 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(), ¶ms, 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/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 620c18728..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; } @@ -166,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 5578181a4..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; @@ -41,7 +42,7 @@ 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, @@ -119,7 +120,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult } 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() { @@ -154,7 +155,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) { } free_buffers_condition.notify_one(); - buffer_wait_event.writable->Signal(); + buffer_wait_event->GetWritableEvent()->Signal(); } void BufferQueue::Connect() { @@ -169,7 +170,7 @@ void BufferQueue::Disconnect() { std::unique_lock lock{queue_sequence_mutex}; queue_sequence.clear(); } - buffer_wait_event.writable->Signal(); + buffer_wait_event->GetWritableEvent()->Signal(); is_connect = false; free_buffers_condition.notify_one(); } @@ -188,12 +189,12 @@ u32 BufferQueue::Query(QueryType 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 ad7469277..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,7 +129,7 @@ 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 free_buffers_mutex; std::condition_variable free_buffers_condition; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index ceaa93d28..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" @@ -165,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/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/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 2b824059d..0b306b87a 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); @@ -812,7 +841,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 +855,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/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" diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index ccf8cc153..f976d0a9c 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -15,9 +15,9 @@ #include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs_offset.h" #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/nro.h" #include "core/loader/nso.h" @@ -219,8 +219,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::Sy } is_loaded = true; - return {ResultStatus::Success, - LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; + return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, + Core::Memory::DEFAULT_STACK_SIZE}}; } ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 95b6f339a..ea347ea83 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -15,9 +15,9 @@ #include "core/core.h" #include "core/file_sys/patch_manager.h" #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" #include "core/loader/nso.h" #include "core/memory.h" #include "core/settings.h" @@ -179,8 +179,8 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::Sy LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); is_loaded = true; - return {ResultStatus::Success, - LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; + return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, + Core::Memory::DEFAULT_STACK_SIZE}}; } ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) { diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 0becdf642..f199c3362 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -20,7 +20,6 @@ #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" #include "core/hle/result.h" -#include "core/hle/service/lm/manager.h" #include "core/memory.h" #include "core/reporter.h" #include "core/settings.h" @@ -360,55 +359,6 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result, SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); } -void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const { - if (!IsReportingEnabled()) { - return; - } - - const auto timestamp = GetTimestamp(); - json out; - - out["yuzu_version"] = GetYuzuVersionData(); - out["report_common"] = - GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp); - - out["log_destination"] = - fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination)); - - auto json_messages = json::array(); - std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages), - [](const Service::LM::LogMessage& message) { - json out; - out["is_head"] = fmt::format("{}", message.header.IsHeadLog()); - out["is_tail"] = fmt::format("{}", message.header.IsTailLog()); - out["pid"] = fmt::format("{:016X}", message.header.pid); - out["thread_context"] = - fmt::format("{:016X}", message.header.thread_context); - out["payload_size"] = fmt::format("{:016X}", message.header.payload_size); - out["flags"] = fmt::format("{:04X}", message.header.flags.Value()); - out["severity"] = fmt::format("{}", message.header.severity.Value()); - out["verbosity"] = fmt::format("{:02X}", message.header.verbosity); - - auto fields = json::array(); - std::transform(message.fields.begin(), message.fields.end(), - std::back_inserter(fields), [](const auto& kv) { - json out; - out["type"] = fmt::format("{}", kv.first); - out["data"] = - Service::LM::FormatField(kv.first, kv.second); - return out; - }); - - out["fields"] = std::move(fields); - return out; - }); - - out["log_messages"] = std::move(json_messages); - - SaveToFile(std::move(out), - GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp)); -} - void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, std::string log_message) const { if (!IsReportingEnabled()) diff --git a/src/core/reporter.h b/src/core/reporter.h index 86d760cf0..b2c2d9a2e 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -72,9 +72,6 @@ public: void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, std::string log_message) const; - // Used by lm services - void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const; - // Can be used anywhere to generate a backtrace and general info report at any point during // execution. Not intended to be used for anything other than debugging or testing. void SaveUserReport() const; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 39306509a..2ae5196e0 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -70,6 +70,9 @@ void LogSettings() { log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); log_setting("Audio_OutputDevice", values.audio_device_id); log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); + log_setting("DataStorage_CacheDir", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)); + log_setting("DataStorage_ConfigDir", Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); + log_setting("DataStorage_LoadDir", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir)); log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)); log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)); log_setting("Debugging_ProgramArgs", values.program_args); diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index 40b516f85..770893687 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <atomic> #include <chrono> #include <cmath> #include <thread> @@ -20,13 +21,16 @@ public: : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), modifier_angle(modifier_angle_) { + update_thread_running.store(true); update_thread = std::thread(&Analog::UpdateStatus, this); } ~Analog() override { - update_thread_running = false; - if (update_thread.joinable()) { - update_thread.join(); + if (update_thread_running.load()) { + update_thread_running.store(false); + if (update_thread.joinable()) { + update_thread.join(); + } } } @@ -58,7 +62,7 @@ public: } void UpdateStatus() { - while (update_thread_running) { + while (update_thread_running.load()) { const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; bool r = right->GetStatus(); @@ -135,6 +139,10 @@ public: static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF)); } + Input::AnalogProperties GetAnalogProperties() const override { + return {modifier_scale, 1.0f, 0.5f}; + } + bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { switch (direction) { case Input::AnalogDirection::RIGHT: @@ -160,7 +168,7 @@ private: float angle{}; float amplitude{}; std::thread update_thread; - bool update_thread_running{true}; + std::atomic<bool> update_thread_running{}; }; std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 9670bdeb2..1b6ded8d6 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -185,6 +185,16 @@ public: return {0.0f, 0.0f}; } + std::tuple<float, float> GetRawStatus() const override { + const float x = GetAxis(axis_x); + const float y = GetAxis(axis_y); + return {x, y}; + } + + Input::AnalogProperties GetAnalogProperties() const override { + return {deadzone, range, 0.5f}; + } + bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { const auto [x, y] = GetStatus(); const float directional_deadzone = 0.5f; diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index 508eb0c7d..3d799b293 100644 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -106,6 +106,16 @@ public: return {0.0f, 0.0f}; } + std::tuple<float, float> GetRawStatus() const override { + const float x = GetAxis(axis_x); + const float y = GetAxis(axis_y); + return {x, y}; + } + + Input::AnalogProperties GetAnalogProperties() const override { + return {deadzone, range, 0.5f}; + } + private: const u32 button; const u32 axis_x; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d32eb732a..f67de37e3 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -81,10 +81,14 @@ public: } bool RumblePlay(u16 amp_low, u16 amp_high) { + constexpr u32 rumble_max_duration_ms = 1000; + if (sdl_controller) { - return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; + return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, + rumble_max_duration_ms) == 0; } else if (sdl_joystick) { - return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; + return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, + rumble_max_duration_ms) == 0; } return false; @@ -373,6 +377,16 @@ public: return {}; } + std::tuple<float, float> GetRawStatus() const override { + const float x = joystick->GetAxis(axis_x, range); + const float y = joystick->GetAxis(axis_y, range); + return {x, -y}; + } + + Input::AnalogProperties GetAnalogProperties() const override { + return {deadzone, range, 0.5f}; + } + bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { const auto [x, y] = GetStatus(); const float directional_deadzone = 0.5f; diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index a07124a86..ffbe4f2ed 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -25,18 +25,19 @@ public: } } - std::tuple<float, float, bool> GetStatus() const override { - for (const auto& m : map) { - const bool state = std::get<0>(m)->GetStatus(); + Input::TouchStatus GetStatus() const override { + Input::TouchStatus touch_status{}; + for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) { + const bool state = std::get<0>(map[id])->GetStatus(); if (state) { - const float x = static_cast<float>(std::get<1>(m)) / + const float x = static_cast<float>(std::get<1>(map[id])) / static_cast<int>(Layout::ScreenUndocked::Width); - const float y = static_cast<float>(std::get<2>(m)) / + const float y = static_cast<float>(std::get<2>(map[id])) / static_cast<int>(Layout::ScreenUndocked::Height); - return {x, y, true}; + touch_status[id] = {x, y, true}; } } - return {}; + return touch_status; } private: diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 412d57896..e7e50d789 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -136,6 +136,7 @@ static void SocketLoop(Socket* socket) { Client::Client() { LOG_INFO(Input, "Udp Initialization started"); + finger_id.fill(MAX_TOUCH_FINGERS); ReloadSockets(); } @@ -176,7 +177,7 @@ void Client::ReloadSockets() { std::string server_token; std::size_t client = 0; while (std::getline(servers_ss, server_token, ',')) { - if (client == max_udp_clients) { + if (client == MAX_UDP_CLIENTS) { break; } std::stringstream server_ss(server_token); @@ -194,7 +195,7 @@ void Client::ReloadSockets() { for (std::size_t pad = 0; pad < 4; ++pad) { const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port, pad); - if (client_number != max_udp_clients) { + if (client_number != MAX_UDP_CLIENTS) { LOG_ERROR(Input, "Duplicated UDP servers found"); continue; } @@ -213,7 +214,7 @@ std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t return client; } } - return max_udp_clients; + return MAX_UDP_CLIENTS; } void Client::OnVersion([[maybe_unused]] Response::Version data) { @@ -259,33 +260,14 @@ void Client::OnPadData(Response::PadData data, std::size_t client) { std::lock_guard guard(clients[client].status.update_mutex); clients[client].status.motion_status = clients[client].motion.GetMotion(); - // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates - // between a simple "tap" and a hard press that causes the touch screen to click. - const bool is_active = data.touch_1.is_active != 0; - - float x = 0; - float y = 0; - - if (is_active && clients[client].status.touch_calibration) { - const u16 min_x = clients[client].status.touch_calibration->min_x; - const u16 max_x = clients[client].status.touch_calibration->max_x; - const u16 min_y = clients[client].status.touch_calibration->min_y; - const u16 max_y = clients[client].status.touch_calibration->max_y; - - x = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - - min_x) / - static_cast<float>(max_x - min_x); - y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) - - min_y) / - static_cast<float>(max_y - min_y); + for (std::size_t id = 0; id < data.touch.size(); ++id) { + UpdateTouchInput(data.touch[id], client, id); } - clients[client].status.touch_status = {x, y, is_active}; - if (configuring) { const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); - UpdateYuzuSettings(client, accelerometer, gyroscope, is_active); + UpdateYuzuSettings(client, accelerometer, gyroscope); } } } @@ -320,21 +302,17 @@ void Client::Reset() { } void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro, bool touch) { + const Common::Vec3<float>& gyro) { if (gyro.Length() > 0.2f) { - LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}", - client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch); + LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client, + gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]); } UDPPadStatus pad{ .host = clients[client].host, .port = clients[client].port, .pad_index = clients[client].pad_index, }; - if (touch) { - pad.touch = PadTouch::Click; - pad_queue.Push(pad); - } - for (size_t i = 0; i < 3; ++i) { + for (std::size_t i = 0; i < 3; ++i) { if (gyro[i] > 5.0f || gyro[i] < -5.0f) { pad.motion = static_cast<PadMotion>(i); pad.motion_value = gyro[i]; @@ -348,6 +326,50 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& a } } +std::optional<std::size_t> Client::GetUnusedFingerID() const { + std::size_t first_free_id = 0; + while (first_free_id < MAX_TOUCH_FINGERS) { + if (!std::get<2>(touch_status[first_free_id])) { + return first_free_id; + } else { + first_free_id++; + } + } + return std::nullopt; +} + +void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) { + // TODO: Use custom calibration per device + const Common::ParamPackage touch_param(Settings::values.touch_device); + const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100)); + const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50)); + const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800)); + const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850)); + const std::size_t touch_id = client * 2 + id; + if (touch_pad.is_active) { + if (finger_id[touch_id] == MAX_TOUCH_FINGERS) { + const auto first_free_id = GetUnusedFingerID(); + if (!first_free_id) { + // Invalid finger id skip to next input + return; + } + finger_id[touch_id] = *first_free_id; + } + auto& [x, y, pressed] = touch_status[finger_id[touch_id]]; + x = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) / + static_cast<float>(max_x - min_x); + y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) / + static_cast<float>(max_y - min_y); + pressed = true; + return; + } + + if (finger_id[touch_id] != MAX_TOUCH_FINGERS) { + touch_status[finger_id[touch_id]] = {}; + finger_id[touch_id] = MAX_TOUCH_FINGERS; + } +} + void Client::BeginConfiguration() { pad_queue.Clear(); configuring = true; @@ -360,7 +382,7 @@ void Client::EndConfiguration() { DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == max_udp_clients) { + if (client_number == MAX_UDP_CLIENTS) { return clients[0].status; } return clients[client_number].status; @@ -368,12 +390,20 @@ DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == max_udp_clients) { + if (client_number == MAX_UDP_CLIENTS) { return clients[0].status; } return clients[client_number].status; } +Input::TouchStatus& Client::GetTouchState() { + return touch_status; +} + +const Input::TouchStatus& Client::GetTouchState() const { + return touch_status; +} + Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() { return pad_queue; } @@ -426,24 +456,24 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( current_status = Status::Ready; status_callback(current_status); } - if (data.touch_1.is_active == 0) { + if (data.touch[0].is_active == 0) { return; } - LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, - data.touch_1.y); - min_x = std::min(min_x, static_cast<u16>(data.touch_1.x)); - min_y = std::min(min_y, static_cast<u16>(data.touch_1.y)); + LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, + data.touch[0].y); + min_x = std::min(min_x, static_cast<u16>(data.touch[0].x)); + min_y = std::min(min_y, static_cast<u16>(data.touch[0].y)); if (current_status == Status::Ready) { // First touch - min data (min_x/min_y) current_status = Status::Stage1Completed; status_callback(current_status); } - if (data.touch_1.x - min_x > CALIBRATION_THRESHOLD && - data.touch_1.y - min_y > CALIBRATION_THRESHOLD) { + if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && + data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { // Set the current position as max value and finishes // configuration - max_x = data.touch_1.x; - max_y = data.touch_1.y; + max_x = data.touch[0].x; + max_y = data.touch[0].y; current_status = Status::Completed; data_callback(min_x, min_y, max_x, max_y); status_callback(current_status); diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 00c8b09f5..822f9c550 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -28,6 +28,7 @@ class Socket; namespace Response { struct PadData; struct PortInfo; +struct TouchPad; struct Version; } // namespace Response @@ -50,7 +51,6 @@ struct UDPPadStatus { std::string host{"127.0.0.1"}; u16 port{26760}; std::size_t pad_index{}; - PadTouch touch{PadTouch::Undefined}; PadMotion motion{PadMotion::Undefined}; f32 motion_value{0.0f}; }; @@ -93,6 +93,9 @@ public: DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad); const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const; + Input::TouchStatus& GetTouchState(); + const Input::TouchStatus& GetTouchState() const; + private: struct ClientData { std::string host{"127.0.0.1"}; @@ -122,14 +125,25 @@ private: void StartCommunication(std::size_t client, const std::string& host, u16 port, std::size_t pad_index, u32 client_id); void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro, bool touch); + const Common::Vec3<float>& gyro); + + // Returns an unused finger id, if there is no fingers available std::nullopt will be + // returned + std::optional<std::size_t> GetUnusedFingerID() const; + + // Merges and updates all touch inputs into the touch_status array + void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id); bool configuring = false; // Allocate clients for 8 udp servers - const std::size_t max_udp_clients = 32; - std::array<ClientData, 4 * 8> clients; - Common::SPSCQueue<UDPPadStatus> pad_queue; + static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8; + // Each client can have up 2 touch inputs + static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; + std::array<ClientData, MAX_UDP_CLIENTS> clients{}; + Common::SPSCQueue<UDPPadStatus> pad_queue{}; + Input::TouchStatus touch_status{}; + std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{}; }; /// An async job allowing configuration of the touchpad calibration. diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index fc1aea4b9..a3d276697 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h @@ -140,6 +140,14 @@ static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong si static_assert(std::is_trivially_copyable_v<PortInfo>, "UDP Response PortInfo is not trivially copyable"); +struct TouchPad { + u8 is_active{}; + u8 id{}; + u16_le x{}; + u16_le y{}; +}; +static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size "); + #pragma pack(push, 1) struct PadData { PortInfo info{}; @@ -190,12 +198,7 @@ struct PadData { u8 button_13{}; } analog_button; - struct TouchPad { - u8 is_active{}; - u8 id{}; - u16_le x{}; - u16_le y{}; - } touch_1, touch_2; + std::array<TouchPad, 2> touch; u64_le motion_timestamp; @@ -222,7 +225,6 @@ static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE, static_assert(sizeof(PadData::AnalogButton) == 12, "UDP Response AnalogButton struct has wrong size "); -static_assert(sizeof(PadData::TouchPad) == 6, "UDP Response TouchPad struct has wrong size "); static_assert(sizeof(PadData::Accelerometer) == 12, "UDP Response Accelerometer struct has wrong size "); static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size "); diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index c5da27a38..b630281a0 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -78,8 +78,8 @@ public: explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} - std::tuple<float, float, bool> GetStatus() const override { - return client->GetPadState(ip, port, pad).touch_status; + Input::TouchStatus GetStatus() const override { + return client->GetTouchState(); } private: @@ -107,32 +107,4 @@ std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamP return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get()); } -void UDPTouchFactory::BeginConfiguration() { - polling = true; - client->BeginConfiguration(); -} - -void UDPTouchFactory::EndConfiguration() { - polling = false; - client->EndConfiguration(); -} - -Common::ParamPackage UDPTouchFactory::GetNextInput() { - Common::ParamPackage params; - CemuhookUDP::UDPPadStatus pad; - auto& queue = client->GetPadQueue(); - while (queue.Pop(pad)) { - if (pad.touch == CemuhookUDP::PadTouch::Undefined) { - continue; - } - params.Set("engine", "cemuhookudp"); - params.Set("ip", pad.host); - params.Set("port", static_cast<u16>(pad.port)); - params.Set("pad_index", static_cast<u16>(pad.pad_index)); - params.Set("touch", static_cast<u16>(pad.touch)); - return params; - } - return params; -} - } // namespace InputCommon diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index 54def22da..903626e4b 100644 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp @@ -14,7 +14,7 @@ namespace Common { TEST_CASE("RingBuffer: Basic Tests", "[common]") { - RingBuffer<char, 4, 1> buf; + RingBuffer<char, 4> buf; // Pushing values into a ring buffer with space should succeed. for (std::size_t i = 0; i < 4; i++) { @@ -77,7 +77,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { } TEST_CASE("RingBuffer: Threaded Test", "[common]") { - RingBuffer<char, 4, 2> buf; + RingBuffer<char, 8> buf; const char seed = 42; const std::size_t count = 1000000; std::size_t full = 0; @@ -92,8 +92,8 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { std::array<char, 2> value = {seed, seed}; std::size_t i = 0; while (i < count) { - if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { - REQUIRE(c == 1U); + if (const std::size_t c = buf.Push(&value[0], 2); c > 0) { + REQUIRE(c == 2U); i++; next_value(value); } else { @@ -107,7 +107,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { std::array<char, 2> value = {seed, seed}; std::size_t i = 0; while (i < count) { - if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { + if (const std::vector<char> v = buf.Pop(2); v.size() > 0) { REQUIRE(v.size() == 2U); REQUIRE(v[0] == value[0]); REQUIRE(v[1] == value[1]); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e01ea55ab..2cf95937e 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -67,8 +67,6 @@ add_library(video_core STATIC guest_driver.h memory_manager.cpp memory_manager.h - morton.cpp - morton.h query_cache.h rasterizer_accelerated.cpp rasterizer_accelerated.h @@ -288,10 +286,10 @@ target_link_libraries(video_core PRIVATE sirit) if (ENABLE_NSIGHT_AFTERMATH) if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) - message(ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided") + message(FATAL_ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided") endif() if (NOT WIN32) - message(ERROR "Nsight Aftermath doesn't support non-Windows platforms") + message(FATAL_ERROR "Nsight Aftermath doesn't support non-Windows platforms") endif() target_compile_definitions(video_core PRIVATE HAS_NSIGHT_AFTERMATH) target_include_directories(video_core PRIVATE "$ENV{NSIGHT_AFTERMATH_SDK}/include") diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index fd740c2c1..ee8602ce9 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -446,7 +446,7 @@ private: * @param offset Offset in bytes from the start of the buffer * @param size Size in bytes of the region to query for modifications * - * @tparam True to query GPU modified pages, false for CPU pages + * @tparam gpu True to query GPU modified pages, false for CPU pages */ template <bool gpu> [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept { diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 9be651e24..116ad1722 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -179,22 +179,22 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume return ProcessMacroBind(argument); case MAXWELL3D_REG_INDEX(firmware[4]): return ProcessFirmwareCall4(); - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: return StartCBData(method); case MAXWELL3D_REG_INDEX(cb_bind[0]): return ProcessCBBind(0); @@ -287,22 +287,22 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, return; } switch (method) { - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data): + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: + case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: ProcessCBMultiData(method, base_start, amount); break; default: @@ -592,7 +592,7 @@ void Maxwell3D::ProcessCBData(u32 value) { } void Maxwell3D::StartCBData(u32 method) { - constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]); + constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data); cb_data_state.start_pos = regs.const_buffer.cb_pos; cb_data_state.id = method - first_cb_data; cb_data_state.current = method; @@ -605,7 +605,7 @@ void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount if (cb_data_state.current != null_cb_data) { FinishCBData(); } - constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]); + constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data); cb_data_state.start_pos = regs.const_buffer.cb_pos; cb_data_state.id = method - first_cb_data; cb_data_state.current = method; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 326b32228..002d1b3f9 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1337,7 +1337,7 @@ public: u32 cb_address_high; u32 cb_address_low; u32 cb_pos; - u32 cb_data[NumCBData]; + std::array<u32, NumCBData> cb_data; GPUVAddr BufferAddress() const { return static_cast<GPUVAddr>( diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 4c7399d5a..28f2b8614 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -20,6 +20,7 @@ set(SHADER_FILES find_program(GLSLANGVALIDATOR "glslangValidator" REQUIRED) set(GLSL_FLAGS "") +set(QUIET_FLAG "--quiet") set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders) @@ -28,6 +29,23 @@ set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE) set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in) set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake) +# Check if `--quiet` is available on host's glslangValidator version +# glslangValidator prints to STDERR iff an unrecognized flag is passed to it +execute_process( + COMMAND + ${GLSLANGVALIDATOR} ${QUIET_FLAG} + ERROR_VARIABLE + GLSLANG_ERROR + # STDOUT variable defined to silence unnecessary output during CMake configuration + OUTPUT_VARIABLE + GLSLANG_OUTPUT +) + +if (NOT GLSLANG_ERROR STREQUAL "") + message(WARNING "Refusing to use unavailable flag `${QUIET_FLAG}` on `${GLSLANGVALIDATOR}`") + set(QUIET_FLAG "") +endif() + foreach(FILENAME IN ITEMS ${SHADER_FILES}) string(REPLACE "." "_" SHADER_NAME ${FILENAME}) set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}) @@ -55,7 +73,7 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES}) OUTPUT ${SPIRV_HEADER_FILE} COMMAND - ${GLSLANGVALIDATOR} -V ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} + ${GLSLANGVALIDATOR} -V ${QUIET_FLAG} ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} MAIN_DEPENDENCY ${SOURCE_FILE} ) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 65feff588..c841f3cd7 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -4,6 +4,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" @@ -38,6 +39,12 @@ GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std } GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { + const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); + if (it != map_ranges.end() && it->first == gpu_addr) { + it->second = size; + } else { + map_ranges.insert(it, MapRange{gpu_addr, size}); + } return UpdateRange(gpu_addr, cpu_addr, size); } @@ -52,10 +59,16 @@ GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) { } void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { - if (!size) { + if (size == 0) { return; } - + const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); + if (it != map_ranges.end()) { + ASSERT(it->first == gpu_addr); + map_ranges.erase(it); + } else { + UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr); + } // Flush and invalidate through the GPU interface, to be asynchronous if possible. const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); @@ -218,6 +231,12 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { return system.Memory().GetPointer(*address); } +size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept { + auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first); + --it; + return it->second - (gpu_addr - it->first); +} + void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { std::size_t remaining_size{size}; std::size_t page_index{gpu_src_addr >> page_bits}; @@ -314,17 +333,29 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buf } } +void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { + size_t remaining_size{size}; + size_t page_index{gpu_addr >> page_bits}; + size_t page_offset{gpu_addr & page_mask}; + while (remaining_size > 0) { + const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; + if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { + rasterizer->FlushRegion(*page_addr + page_offset, num_bytes); + } + ++page_index; + page_offset = 0; + remaining_size -= num_bytes; + } +} + void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { std::vector<u8> tmp_buffer(size); ReadBlock(gpu_src_addr, tmp_buffer.data(), size); - WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); -} -void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, - std::size_t size) { - std::vector<u8> tmp_buffer(size); - ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size); - WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); + // The output block must be flushed in case it has data modified from the GPU. + // Fixes NPC geometry in Zombie Panic in Wonderland DX + FlushRegion(gpu_dest_addr, size); + WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); } bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index c35e57689..b468a67de 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -85,6 +85,9 @@ public: [[nodiscard]] u8* GetPointer(GPUVAddr addr); [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; + /// Returns the number of bytes until the end of the memory map containing the given GPU address + [[nodiscard]] size_t BytesToMapEnd(GPUVAddr gpu_addr) const noexcept; + /** * ReadBlock and WriteBlock are full read and write operations over virtual * GPU Memory. It's important to use these when GPU memory may not be continuous @@ -107,7 +110,6 @@ public: */ void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); - void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); /** * IsGranularRange checks if a gpu region can be simply read with a pointer. @@ -131,6 +133,8 @@ private: void TryLockPage(PageEntry page_entry, std::size_t size); void TryUnlockPage(PageEntry page_entry, std::size_t size); + void FlushRegion(GPUVAddr gpu_addr, size_t size) const; + [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { return (gpu_addr >> page_bits) & page_table_mask; } @@ -150,6 +154,9 @@ private: VideoCore::RasterizerInterface* rasterizer = nullptr; std::vector<PageEntry> page_table; + + using MapRange = std::pair<GPUVAddr, size_t>; + std::vector<MapRange> map_ranges; }; } // namespace Tegra diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp deleted file mode 100644 index e69de29bb..000000000 --- a/src/video_core/morton.cpp +++ /dev/null diff --git a/src/video_core/morton.h b/src/video_core/morton.h deleted file mode 100644 index e69de29bb..000000000 --- a/src/video_core/morton.h +++ /dev/null diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 81b71edfb..04c267ee4 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -246,6 +246,7 @@ Device::Device() GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2; use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue(); + use_driver_cache = is_nvidia; LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 3e79d1e37..9141de635 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -120,6 +120,10 @@ public: return use_asynchronous_shaders; } + bool UseDriverCache() const { + return use_driver_cache; + } + private: static bool TestVariableAoffi(); static bool TestPreciseBug(); @@ -147,6 +151,7 @@ private: bool has_debugging_tool_attached{}; bool use_assembly_shaders{}; bool use_asynchronous_shaders{}; + bool use_driver_cache{}; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index d4841fdb7..529570ff0 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -159,6 +159,10 @@ std::unordered_set<GLenum> GetSupportedFormats() { ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 unique_identifier, const ShaderIR& ir, const Registry& registry, bool hint_retrievable) { + if (device.UseDriverCache()) { + // Ignore hint retrievable if we are using the driver cache + hint_retrievable = false; + } const std::string shader_id = MakeShaderID(unique_identifier, shader_type); LOG_INFO(Render_OpenGL, "{}", shader_id); @@ -336,7 +340,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop } std::vector<ShaderDiskCachePrecompiled> gl_cache; - if (!device.UseAssemblyShaders()) { + if (!device.UseAssemblyShaders() && !device.UseDriverCache()) { // Only load precompiled cache when we are not using assembly shaders gl_cache = disk_cache.LoadPrecompiled(); } @@ -356,8 +360,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop std::atomic_bool gl_cache_failed = false; const auto find_precompiled = [&gl_cache](u64 id) { - return std::find_if(gl_cache.begin(), gl_cache.end(), - [id](const auto& entry) { return entry.unique_identifier == id; }); + return std::ranges::find(gl_cache, id, &ShaderDiskCachePrecompiled::unique_identifier); }; const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, @@ -432,8 +435,8 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop return; } - if (device.UseAssemblyShaders()) { - // Don't store precompiled binaries for assembly shaders. + if (device.UseAssemblyShaders() || device.UseDriverCache()) { + // Don't store precompiled binaries for assembly shaders or when using the driver cache return; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 2e1fa252d..c35b71b6b 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -14,6 +14,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" +#include "common/div_ceil.h" #include "common/logging/log.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/shader_type.h" @@ -877,7 +878,7 @@ private: u32 binding = device.GetBaseBindings(stage).uniform_buffer; for (const auto& [index, info] : ir.GetConstantBuffers()) { - const u32 num_elements = Common::AlignUp(info.GetSize(), 4) / 4; + const u32 num_elements = Common::DivCeil(info.GetSize(), 4 * sizeof(u32)); const u32 size = info.IsIndirect() ? MAX_CONSTBUFFER_ELEMENTS : num_elements; code.AddLine("layout (std140, binding = {}) uniform {} {{", binding++, GetConstBufferBlock(index)); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index dd77a543c..21159e498 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -506,7 +506,7 @@ bool RendererOpenGL::Init() { AddTelemetryFields(); - if (!GLAD_GL_VERSION_4_3) { + if (!GLAD_GL_VERSION_4_6) { return false; } diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index ca7c2c579..85121d9fd 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -110,8 +110,8 @@ VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_ } // namespace Sampler namespace { - -enum : u32 { Attachable = 1, Storage = 2 }; +constexpr u32 Attachable = 1 << 0; +constexpr u32 Storage = 1 << 1; struct FormatTuple { VkFormat format; ///< Vulkan format @@ -222,22 +222,27 @@ constexpr bool IsZetaFormat(PixelFormat pixel_format) { } // Anonymous namespace -FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format) { - ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples)); - - auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)]; +FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with_srgb, + PixelFormat pixel_format) { + ASSERT(static_cast<size_t>(pixel_format) < std::size(tex_format_tuples)); + FormatTuple tuple = tex_format_tuples[static_cast<size_t>(pixel_format)]; if (tuple.format == VK_FORMAT_UNDEFINED) { UNIMPLEMENTED_MSG("Unimplemented texture format with pixel format={}", pixel_format); - return {VK_FORMAT_A8B8G8R8_UNORM_PACK32, true, true}; + return FormatInfo{VK_FORMAT_A8B8G8R8_UNORM_PACK32, true, true}; } // Use A8B8G8R8_UNORM on hardware that doesn't support ASTC natively if (!device.IsOptimalAstcSupported() && VideoCore::Surface::IsPixelFormatASTC(pixel_format)) { - const bool is_srgb = VideoCore::Surface::IsPixelFormatSRGB(pixel_format); - tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32; + const bool is_srgb = with_srgb && VideoCore::Surface::IsPixelFormatSRGB(pixel_format); + if (is_srgb) { + tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32; + } else { + tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; + tuple.usage |= Storage; + } } - const bool attachable = tuple.usage & Attachable; - const bool storage = tuple.usage & Storage; + const bool attachable = (tuple.usage & Attachable) != 0; + const bool storage = (tuple.usage & Storage) != 0; VkFormatFeatureFlags usage{}; switch (format_type) { @@ -671,7 +676,7 @@ VkFrontFace FrontFace(Maxwell::FrontFace front_face) { return {}; } -VkCullModeFlags CullFace(Maxwell::CullFace cull_face) { +VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face) { switch (cull_face) { case Maxwell::CullFace::Front: return VK_CULL_MODE_FRONT_BIT; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h index 537969840..7c34b47dc 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.h +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h @@ -35,7 +35,15 @@ struct FormatInfo { bool storage; }; -FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format); +/** + * Returns format properties supported in the host + * @param device Host device + * @param format_type Type of image the buffer will use + * @param with_srgb True when the format can be sRGB when converted to another format (ASTC) + * @param pixel_format Guest pixel format to describe + */ +[[nodiscard]] FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with_srgb, + PixelFormat pixel_format); VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage); @@ -55,7 +63,7 @@ VkBlendFactor BlendFactor(Maxwell::Blend::Factor factor); VkFrontFace FrontFace(Maxwell::FrontFace front_face); -VkCullModeFlags CullFace(Maxwell::CullFace cull_face); +VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face); VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index a5214d0bc..d50dca604 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -181,6 +181,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules( .pNext = nullptr, .flags = 0, .codeSize = 0, + .pCode = nullptr, }; std::vector<vk::ShaderModule> shader_modules; @@ -326,8 +327,8 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program, .rasterizerDiscardEnable = static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = - dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE, + .cullMode = static_cast<VkCullModeFlags>( + dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), .depthBiasEnable = state.depth_bias_enable, .depthBiasConstantFactor = 0.0f, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 02282e36f..8991505ca 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -355,14 +355,12 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) { SPIRVProgram program; std::vector<VkDescriptorSetLayoutBinding> bindings; - for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + for (std::size_t index = 1; index < Maxwell::MaxShaderProgram; ++index) { const auto program_enum = static_cast<Maxwell::ShaderProgram>(index); - // Skip stages that are not enabled if (!maxwell3d.regs.IsShaderConfigEnabled(index)) { continue; } - const GPUVAddr gpu_addr = GetShaderAddress(maxwell3d, program_enum); const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get(); @@ -372,12 +370,8 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) { const auto& entries = shader->GetEntries(); program[stage] = { Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization), - entries}; - - if (program_enum == Maxwell::ShaderProgram::VertexA) { - // VertexB was combined with VertexA, so we skip the VertexB iteration - ++index; - } + entries, + }; const u32 old_binding = specialization.base_binding; specialization.base_binding = diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 4cd43e425..15f2987eb 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -6,10 +6,12 @@ #include <atomic> #include <condition_variable> +#include <cstddef> #include <memory> #include <stack> #include <thread> #include <utility> +#include "common/alignment.h" #include "common/common_types.h" #include "common/threadsafe_queue.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -130,12 +132,11 @@ private: using FuncType = TypedCommand<T>; static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); + command_offset = Common::AlignUp(command_offset, alignof(FuncType)); if (command_offset > sizeof(data) - sizeof(FuncType)) { return false; } - - Command* current_last = last; - + Command* const current_last = last; last = new (data.data() + command_offset) FuncType(std::move(command)); if (current_last) { @@ -143,7 +144,6 @@ private: } else { first = last; } - command_offset += sizeof(FuncType); return true; } @@ -156,8 +156,8 @@ private: Command* first = nullptr; Command* last = nullptr; - std::size_t command_offset = 0; - std::array<u8, 0x8000> data{}; + size_t command_offset = 0; + alignas(std::max_align_t) std::array<u8, 0x8000> data{}; }; struct State { diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 89cbe01ad..61d52b961 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1334,7 +1334,10 @@ private: } if (const auto comment = std::get_if<CommentNode>(&*node)) { - Name(OpUndef(t_void), comment->GetText()); + if (device.HasDebuggingToolAttached()) { + // We should insert comments with OpString instead of using named variables + Name(OpUndef(t_int), comment->GetText()); + } return {}; } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index ab14922d7..aa7c5d7c6 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -95,20 +95,12 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { } } -[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) { - const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, info.format); - VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - if (info.type == ImageType::e2D && info.resources.layers >= 6 && - info.size.width == info.size.height) { - flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - } - if (info.type == ImageType::e3D) { - flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; - } +[[nodiscard]] VkImageUsageFlags ImageUsageFlags(const MaxwellToVK::FormatInfo& info, + PixelFormat format) { VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - if (format_info.attachable) { - switch (VideoCore::Surface::GetFormatType(info.format)) { + if (info.attachable) { + switch (VideoCore::Surface::GetFormatType(format)) { case VideoCore::Surface::SurfaceType::ColorTexture: usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; break; @@ -120,9 +112,33 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { UNREACHABLE_MSG("Invalid surface type"); } } - if (format_info.storage) { + if (info.storage) { usage |= VK_IMAGE_USAGE_STORAGE_BIT; } + return usage; +} + +/// Returns the preferred format for a VkImage +[[nodiscard]] PixelFormat StorageFormat(PixelFormat format) { + switch (format) { + case PixelFormat::A8B8G8R8_SRGB: + return PixelFormat::A8B8G8R8_UNORM; + default: + return format; + } +} + +[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) { + const PixelFormat format = StorageFormat(info.format); + const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format); + VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + if (info.type == ImageType::e2D && info.resources.layers >= 6 && + info.size.width == info.size.height) { + flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + } + if (info.type == ImageType::e3D) { + flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; + } const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples); return VkImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -130,17 +146,16 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { .flags = flags, .imageType = ConvertImageType(info.type), .format = format_info.format, - .extent = - { - .width = info.size.width >> samples_x, - .height = info.size.height >> samples_y, - .depth = info.size.depth, - }, + .extent{ + .width = info.size.width >> samples_x, + .height = info.size.height >> samples_y, + .depth = info.size.depth, + }, .mipLevels = static_cast<u32>(info.resources.levels), .arrayLayers = static_cast<u32>(info.resources.layers), .samples = ConvertSampleCount(info.num_samples), .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = usage, + .usage = ImageUsageFlags(format_info, format), .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -209,10 +224,11 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { [[nodiscard]] VkAttachmentDescription AttachmentDescription(const Device& device, const ImageView* image_view) { - const auto pixel_format = image_view->format; + using MaxwellToVK::SurfaceFormat; + const PixelFormat pixel_format = image_view->format; return VkAttachmentDescription{ .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, - .format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, pixel_format).format, + .format = SurfaceFormat(device, FormatType::Optimal, true, pixel_format).format, .samples = image_view->Samples(), .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, @@ -868,11 +884,16 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); } } - const VkFormat vk_format = - MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, format).format; + const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format); + const VkFormat vk_format = format_info.format; + const VkImageViewUsageCreateInfo image_view_usage{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, + .pNext = nullptr, + .usage = ImageUsageFlags(format_info, format), + }; const VkImageViewCreateInfo create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, + .pNext = &image_view_usage, .flags = 0, .image = image.Handle(), .viewType = VkImageViewType{}, @@ -962,7 +983,7 @@ vk::ImageView ImageView::MakeDepthStencilView(VkImageAspectFlags aspect_mask) { .flags = 0, .image = image_handle, .viewType = ImageViewType(type), - .format = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, format).format, + .format = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format).format, .components{ .r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index a55d405d1..8d29361a1 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -72,7 +72,7 @@ struct TextureCacheRuntime { MemoryAllocator& memory_allocator; StagingBufferPool& staging_buffer_pool; BlitImageHelper& blit_image_helper; - std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache; + std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache{}; void Finish(); diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp index 9707136e9..3b40db9bc 100644 --- a/src/video_core/shader/async_shaders.cpp +++ b/src/video_core/shader/async_shaders.cpp @@ -129,6 +129,15 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device, .compiler_settings = compiler_settings, .registry = registry, .cpu_address = cpu_addr, + .pp_cache = nullptr, + .vk_device = nullptr, + .scheduler = nullptr, + .descriptor_pool = nullptr, + .update_descriptor_queue = nullptr, + .bindings{}, + .program{}, + .key{}, + .num_color_buffers = 0, }); cv.notify_one(); } @@ -143,6 +152,15 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, std::unique_lock lock(queue_mutex); pending_queue.push({ .backend = Backend::Vulkan, + .device = nullptr, + .shader_type{}, + .uid = 0, + .code{}, + .code_b{}, + .main_offset = 0, + .compiler_settings{}, + .registry{}, + .cpu_address = 0, .pp_cache = pp_cache, .vk_device = &device, .scheduler = &scheduler, diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index ce8fcfe0a..bb2cdef81 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -679,7 +679,7 @@ u32 CalculateLayerSize(const ImageInfo& info) noexcept { } std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets(const ImageInfo& info) noexcept { - ASSERT(info.resources.levels <= MAX_MIP_LEVELS); + ASSERT(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS)); const LevelInfo level_info = MakeLevelInfo(info); std::array<u32, MAX_MIP_LEVELS> offsets{}; u32 offset = 0; @@ -1193,25 +1193,35 @@ u32 MapSizeBytes(const ImageBase& image) { } } -using P = PixelFormat; - -static_assert(CalculateLevelSize(LevelInfo{{1920, 1080}, {0, 2, 0}, {1, 1}, 2, 0}, 0) == 0x7f8000); -static_assert(CalculateLevelSize(LevelInfo{{32, 32}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000); - -static_assert(CalculateLevelOffset(P::R8_SINT, {1920, 1080}, {0, 2}, 1, 0, 7) == 0x2afc00); -static_assert(CalculateLevelOffset(P::ASTC_2D_12X12_UNORM, {8192, 4096}, {0, 2}, 1, 0, 12) == - 0x50d200); - -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 0) == 0); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 1) == 0x400000); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 2) == 0x500000); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 3) == 0x540000); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 4) == 0x550000); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 5) == 0x554000); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 6) == 0x555000); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 7) == 0x555400); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 8) == 0x555600); -static_assert(CalculateLevelOffset(P::A8B8G8R8_UNORM, {1024, 1024}, {0, 4}, 1, 0, 9) == 0x555800); +static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0}, 0) == + 0x7f8000); +static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000); + +static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 1, 0, 7) == + 0x2afc00); +static_assert(CalculateLevelOffset(PixelFormat::ASTC_2D_12X12_UNORM, {8192, 4096, 1}, {0, 2, 0}, 1, + 0, 12) == 0x50d200); + +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 0) == 0); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 1) == 0x400000); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 2) == 0x500000); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 3) == 0x540000); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 4) == 0x550000); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 5) == 0x554000); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 6) == 0x555000); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 7) == 0x555400); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 8) == 0x555600); +static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, + 9) == 0x555800); constexpr u32 ValidateLayerSize(PixelFormat format, u32 width, u32 height, u32 block_height, u32 tile_width_spacing, u32 level) { @@ -1221,13 +1231,14 @@ constexpr u32 ValidateLayerSize(PixelFormat format, u32 width, u32 height, u32 b return AlignLayerSize(offset, size, block, DefaultBlockHeight(format), tile_width_spacing); } -static_assert(ValidateLayerSize(P::ASTC_2D_12X12_UNORM, 8192, 4096, 2, 0, 12) == 0x50d800); -static_assert(ValidateLayerSize(P::A8B8G8R8_UNORM, 1024, 1024, 2, 0, 10) == 0x556000); -static_assert(ValidateLayerSize(P::BC3_UNORM, 128, 128, 2, 0, 8) == 0x6000); +static_assert(ValidateLayerSize(PixelFormat::ASTC_2D_12X12_UNORM, 8192, 4096, 2, 0, 12) == + 0x50d800); +static_assert(ValidateLayerSize(PixelFormat::A8B8G8R8_UNORM, 1024, 1024, 2, 0, 10) == 0x556000); +static_assert(ValidateLayerSize(PixelFormat::BC3_UNORM, 128, 128, 2, 0, 8) == 0x6000); -static_assert(ValidateLayerSize(P::A8B8G8R8_UNORM, 518, 572, 4, 3, 1) == 0x190000, +static_assert(ValidateLayerSize(PixelFormat::A8B8G8R8_UNORM, 518, 572, 4, 3, 1) == 0x190000, "Tile width spacing is not working"); -static_assert(ValidateLayerSize(P::BC5_UNORM, 1024, 1024, 3, 4, 11) == 0x160000, +static_assert(ValidateLayerSize(PixelFormat::BC5_UNORM, 1024, 1024, 3, 4, 11) == 0x160000, "Compressed tile width spacing is not working"); } // namespace VideoCommon diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp index 8d10ac29e..7a9d00d4f 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp @@ -12,21 +12,12 @@ #include <fmt/format.h> -#define VK_NO_PROTOTYPES -#include <vulkan/vulkan.h> - -#include <GFSDK_Aftermath.h> -#include <GFSDK_Aftermath_Defines.h> -#include <GFSDK_Aftermath_GpuCrashDump.h> -#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h> - #include "common/common_paths.h" #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/scope_exit.h" - -#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h" +#include "video_core/vulkan_common/nsight_aftermath_tracker.h" namespace Vulkan { @@ -53,7 +44,7 @@ NsightAftermathTracker::NsightAftermathTracker() { !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON", &GFSDK_Aftermath_GpuCrashDump_GetJSON)) { LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); - return false; + return; } dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash"; diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.h b/src/video_core/vulkan_common/nsight_aftermath_tracker.h index cee3847fb..1ce8d4e8e 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.h +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.h @@ -8,8 +8,9 @@ #include <string> #include <vector> -#define VK_NO_PROTOTYPES -#include <vulkan/vulkan.h> +#include "common/common_types.h" +#include "common/dynamic_library.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" #ifdef HAS_NSIGHT_AFTERMATH #include <GFSDK_Aftermath_Defines.h> @@ -17,9 +18,6 @@ #include <GFSDK_Aftermath_GpuCrashDumpDecoding.h> #endif -#include "common/common_types.h" -#include "common/dynamic_library.h" - namespace Vulkan { class NsightAftermathTracker { diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index ea7af8ad4..5c64c9bf7 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp @@ -39,6 +39,7 @@ vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) { VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, .pfnUserCallback = Callback, + .pUserData = nullptr, }); } diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h index 2efcd244c..b0519f132 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.h +++ b/src/video_core/vulkan_common/vulkan_debug_callback.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 37d7b45a3..51f53bc39 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -84,21 +84,6 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType } } -[[nodiscard]] bool IsRDNA(std::string_view device_name, VkDriverIdKHR driver_id) { - static constexpr std::array RDNA_DEVICES{ - "5700", - "5600", - "5500", - "5300", - }; - if (driver_id != VK_DRIVER_ID_AMD_PROPRIETARY_KHR) { - return false; - } - return std::any_of(RDNA_DEVICES.begin(), RDNA_DEVICES.end(), [device_name](const char* name) { - return device_name.find(name) != std::string_view::npos; - }); -} - std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) { static constexpr std::array formats{ VK_FORMAT_A8B8G8R8_UNORM_PACK32, @@ -436,13 +421,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR "Blacklisting RADV for VK_EXT_extended_dynamic state, likely due to a bug in yuzu"); ext_extended_dynamic_state = false; } - if (ext_extended_dynamic_state && IsRDNA(properties.deviceName, driver_id)) { - // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but on RDNA devices it - // seems to cause stability issues - LOG_WARNING( - Render_Vulkan, - "Blacklisting AMD proprietary on RDNA devices from VK_EXT_extended_dynamic_state"); - ext_extended_dynamic_state = false; + if (is_float16_supported && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { + // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being. + LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math"); + is_float16_supported = false; } graphics_queue = logical.GetQueue(graphics_family); diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index 53b3b275a..9e6cfabf9 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -74,11 +74,10 @@ public: MemoryAllocator(const MemoryAllocator&) = delete; /** - * Commits a memory with the specified requeriments. + * Commits a memory with the specified requirements. * * @param requirements Requirements returned from a Vulkan call. - * @param host_visible Signals the allocator that it *must* use host visible and coherent - * memory. When passing false, it will try to allocate device local memory. + * @param usage Indicates how the memory will be used. * * @returns A memory commit. */ diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index e1bab2112..fb9967c8f 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -71,6 +71,8 @@ add_executable(yuzu configuration/configure_input_player.cpp configuration/configure_input_player.h configuration/configure_input_player.ui + configuration/configure_input_player_widget.cpp + configuration/configure_input_player_widget.h configuration/configure_input_profile_dialog.cpp configuration/configure_input_profile_dialog.h configuration/configure_input_profile_dialog.ui @@ -115,6 +117,8 @@ add_executable(yuzu configuration/input_profiles.h debugger/console.cpp debugger/console.h + debugger/controller.cpp + debugger/controller.h debugger/profiler.cpp debugger/profiler.h debugger/wait_tree.cpp diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 4bf2bfd40..0a4c48b3d 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp @@ -93,7 +93,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) const auto& profiles = profile_manager->GetAllUsers(); for (const auto& user : profiles) { - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!profile_manager->GetProfileBase(user, profile)) continue; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e6c8f18af..ffdf34a4a 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -126,7 +126,7 @@ public: /// Create the original context that should be shared from explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { QSurfaceFormat format; - format.setVersion(4, 3); + format.setVersion(4, 6); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); if (Settings::values.renderer_debug) { @@ -394,7 +394,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { input_subsystem->GetMouse()->PressButton(x, y, event->button()); if (event->button() == Qt::LeftButton) { - this->TouchPressed(x, y); + this->TouchPressed(x, y, 0); } emit MouseActivity(); @@ -409,7 +409,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { auto pos = event->pos(); const auto [x, y] = ScaleTouch(pos); input_subsystem->GetMouse()->MouseMove(x, y); - this->TouchMoved(x, y); + this->TouchMoved(x, y, 0); emit MouseActivity(); } @@ -423,36 +423,72 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { input_subsystem->GetMouse()->ReleaseButton(event->button()); if (event->button() == Qt::LeftButton) { - this->TouchReleased(); + this->TouchReleased(0); } } void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { - // TouchBegin always has exactly one touch point, so take the .first() - const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); - this->TouchPressed(x, y); + QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); + for (const auto& touch_point : touch_points) { + if (!TouchUpdate(touch_point)) { + TouchStart(touch_point); + } + } } void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { - QPointF pos; - int active_points = 0; - - // average all active touch points - for (const auto& tp : event->touchPoints()) { - if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { - active_points++; - pos += tp.pos(); + QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); + for (const auto& touch_point : touch_points) { + if (!TouchUpdate(touch_point)) { + TouchStart(touch_point); + } + } + // Release all inactive points + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (!TouchExist(touch_ids[id], touch_points)) { + touch_ids[id] = 0; + this->TouchReleased(id + 1); } } +} - pos /= active_points; +void GRenderWindow::TouchEndEvent() { + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (touch_ids[id] != 0) { + touch_ids[id] = 0; + this->TouchReleased(id + 1); + } + } +} - const auto [x, y] = ScaleTouch(pos); - this->TouchMoved(x, y); +bool GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) { + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (touch_ids[id] == 0) { + touch_ids[id] = touch_point.id() + 1; + const auto [x, y] = ScaleTouch(touch_point.pos()); + this->TouchPressed(x, y, id + 1); + return true; + } + } + return false; } -void GRenderWindow::TouchEndEvent() { - this->TouchReleased(); +bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) { + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) { + const auto [x, y] = ScaleTouch(touch_point.pos()); + this->TouchMoved(x, y, id + 1); + return true; + } + } + return false; +} + +bool GRenderWindow::TouchExist(std::size_t id, + const QList<QTouchEvent::TouchPoint>& touch_points) const { + return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) { + return id == static_cast<std::size_t>(point.id() + 1); + }); } bool GRenderWindow::event(QEvent* event) { @@ -615,10 +651,10 @@ bool GRenderWindow::LoadOpenGL() { const QString renderer = QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER))); - if (!GLAD_GL_VERSION_4_3) { - LOG_ERROR(Frontend, "GPU does not support OpenGL 4.3: {}", renderer.toStdString()); - QMessageBox::warning(this, tr("Error while initializing OpenGL 4.3!"), - tr("Your GPU may not support OpenGL 4.3, or you do not have the " + if (!GLAD_GL_VERSION_4_6) { + LOG_ERROR(Frontend, "GPU does not support OpenGL 4.6: {}", renderer.toStdString()); + QMessageBox::warning(this, tr("Error while initializing OpenGL 4.6!"), + tr("Your GPU may not support OpenGL 4.6, or you do not have the " "latest graphics driver.<br><br>GL Renderer:<br>%1") .arg(renderer)); return false; @@ -641,26 +677,13 @@ bool GRenderWindow::LoadOpenGL() { QStringList GRenderWindow::GetUnsupportedGLExtensions() const { QStringList unsupported_ext; - if (!GLAD_GL_ARB_buffer_storage) - unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); - if (!GLAD_GL_ARB_direct_state_access) - unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); - if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) - unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); - if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) - unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); - if (!GLAD_GL_ARB_multi_bind) - unsupported_ext.append(QStringLiteral("ARB_multi_bind")); - if (!GLAD_GL_ARB_clip_control) - unsupported_ext.append(QStringLiteral("ARB_clip_control")); - // Extensions required to support some texture formats. - if (!GLAD_GL_EXT_texture_compression_s3tc) + if (!GLAD_GL_EXT_texture_compression_s3tc) { unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); - if (!GLAD_GL_ARB_texture_compression_rgtc) + } + if (!GLAD_GL_ARB_texture_compression_rgtc) { unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); - if (!GLAD_GL_ARB_depth_buffer_float) - unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); + } if (!unsupported_ext.empty()) { LOG_ERROR(Frontend, "GPU does not support all required extensions: {}", diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 339095509..b5ec7de07 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -11,6 +11,7 @@ #include <QImage> #include <QThread> +#include <QTouchEvent> #include <QWidget> #include <QWindow> @@ -21,7 +22,6 @@ class GRenderWindow; class GMainWindow; class QKeyEvent; -class QTouchEvent; class QStringList; namespace InputCommon { @@ -191,6 +191,10 @@ private: void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); + bool TouchStart(const QTouchEvent::TouchPoint& touch_point); + bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point); + bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const; + void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; bool InitializeOpenGL(); @@ -215,6 +219,8 @@ private: bool first_frame = false; + std::array<std::size_t, 16> touch_ids{}; + protected: void showEvent(QShowEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9f73d37d4..8d85a1986 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -464,13 +464,7 @@ void Config::ReadMouseValues() { void Config::ReadTouchscreenValues() { Settings::values.touchscreen.enabled = ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); - Settings::values.touchscreen.device = - ReadSetting(QStringLiteral("touchscreen_device"), QStringLiteral("engine:emu_window")) - .toString() - .toStdString(); - Settings::values.touchscreen.finger = - ReadSetting(QStringLiteral("touchscreen_finger"), 0).toUInt(); Settings::values.touchscreen.rotation_angle = ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); Settings::values.touchscreen.diameter_x = @@ -563,7 +557,8 @@ void Config::ReadMotionTouchValues() { .toString() .toStdString(); Settings::values.touch_device = - ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window")) + ReadSetting(QStringLiteral("touch_device"), + QStringLiteral("min_x:100,min_y:50,max_x:1800,max_y:850")) .toString() .toStdString(); Settings::values.use_touch_from_button = @@ -1088,10 +1083,7 @@ void Config::SaveTouchscreenValues() { const auto& touchscreen = Settings::values.touchscreen; WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); - WriteSetting(QStringLiteral("touchscreen_device"), QString::fromStdString(touchscreen.device), - QStringLiteral("engine:emu_window")); - WriteSetting(QStringLiteral("touchscreen_finger"), touchscreen.finger, 0); WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index fbe36046b..c9d19c948 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -23,6 +23,7 @@ #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_input_player_widget.h" #include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/input_profiles.h" #include "yuzu/util/limitable_input_dialog.h" @@ -254,11 +255,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup}; analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; - const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param, - int default_val, InputCommon::Polling::DeviceType type) { + const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id, + Common::ParamPackage* param, int default_val, + InputCommon::Polling::DeviceType type) { connect(button, &QPushButton::clicked, [=, this] { HandleClick( - button, + button, button_id, [=, this](Common::ParamPackage params) { // Workaround for ZL & ZR for analog triggers like on XBOX // controllers. Analog triggers (from controllers like the XBOX @@ -286,12 +288,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i continue; } - ConfigureButtonClick(button_map[button_id], &buttons_param[button_id], + ConfigureButtonClick(button_map[button_id], button_id, &buttons_param[button_id], Config::default_buttons[button_id], InputCommon::Polling::DeviceType::Button); button->setContextMenuPolicy(Qt::CustomContextMenu); - connect(button, &QPushButton::customContextMenuRequested, [=, this](const QPoint& menu_location) { QMenu context_menu; @@ -300,6 +301,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i button_map[button_id]->setText(tr("[not set]")); }); context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); + ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); }); } @@ -309,7 +311,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i continue; } - ConfigureButtonClick(motion_map[motion_id], &motions_param[motion_id], + ConfigureButtonClick(motion_map[motion_id], motion_id, &motions_param[motion_id], Config::default_motions[motion_id], InputCommon::Polling::DeviceType::Motion); @@ -348,7 +350,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } } HandleClick( - analog_map_buttons[analog_id][sub_button_id], + analog_map_buttons[analog_id][sub_button_id], analog_id, [=, this](const Common::ParamPackage& params) { SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); @@ -358,41 +360,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i analog_button->setContextMenuPolicy(Qt::CustomContextMenu); - connect(analog_button, &QPushButton::customContextMenuRequested, - [=, this](const QPoint& menu_location) { - QMenu context_menu; - context_menu.addAction(tr("Clear"), [&] { - analogs_param[analog_id].Clear(); - analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); - }); - context_menu.addAction(tr("Invert axis"), [&] { - if (sub_button_id == 2 || sub_button_id == 3) { - const bool invert_value = - analogs_param[analog_id].Get("invert_x", "+") == "-"; - const std::string invert_str = invert_value ? "+" : "-"; - analogs_param[analog_id].Set("invert_x", invert_str); - } - if (sub_button_id == 0 || sub_button_id == 1) { - const bool invert_value = - analogs_param[analog_id].Get("invert_y", "+") == "-"; - const std::string invert_str = invert_value ? "+" : "-"; - analogs_param[analog_id].Set("invert_y", invert_str); - } - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; - ++sub_button_id) { - analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( - analogs_param[analog_id], analog_sub_buttons[sub_button_id])); - } - }); - context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( - menu_location)); + connect( + analog_button, &QPushButton::customContextMenuRequested, + [=, this](const QPoint& menu_location) { + QMenu context_menu; + context_menu.addAction(tr("Clear"), [&] { + analogs_param[analog_id].Clear(); + analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); }); + context_menu.addAction(tr("Invert axis"), [&] { + if (sub_button_id == 2 || sub_button_id == 3) { + const bool invert_value = + analogs_param[analog_id].Get("invert_x", "+") == "-"; + const std::string invert_str = invert_value ? "+" : "-"; + analogs_param[analog_id].Set("invert_x", invert_str); + } + if (sub_button_id == 0 || sub_button_id == 1) { + const bool invert_value = + analogs_param[analog_id].Get("invert_y", "+") == "-"; + const std::string invert_str = invert_value ? "+" : "-"; + analogs_param[analog_id].Set("invert_y", invert_str); + } + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; + ++sub_button_id) { + analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( + analogs_param[analog_id], analog_sub_buttons[sub_button_id])); + } + }); + context_menu.exec( + analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(menu_location)); + ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); + }); } // Handle clicks for the modifier buttons as well. connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] { HandleClick( - analog_map_modifier_button[analog_id], + analog_map_modifier_button[analog_id], analog_id, [=, this](const Common::ParamPackage& params) { analogs_param[analog_id].Set("modifier", params.Serialize()); }, @@ -416,12 +420,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i [=, this] { const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); analogs_param[analog_id].Set("range", spinbox_value / 100.0f); + ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); }); connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] { const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); + ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); }); connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] { @@ -433,8 +439,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } // Player Connected checkbox - connect(ui->groupConnectedController, &QGroupBox::toggled, - [this](bool checked) { emit Connected(checked); }); + connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) { + emit Connected(checked); + ui->controllerFrame->SetConnectedStatus(checked); + }); if (player_index == 0) { connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), @@ -553,6 +561,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i // TODO(wwylele): enable this when we actually emulate it ui->buttonHome->setEnabled(false); + ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); + ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked()); } ConfigureInputPlayer::~ConfigureInputPlayer() = default; @@ -580,9 +590,7 @@ void ConfigureInputPlayer::ApplyConfiguration() { if (player_index == 0) { auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; const auto handheld_connected = handheld.connected; - if (player.controller_type == Settings::ControllerType::Handheld) { - handheld = player; - } + handheld = player; handheld.connected = handheld_connected; } } @@ -596,7 +604,7 @@ void ConfigureInputPlayer::TryConnectSelectedController() { controller_type != Settings::ControllerType::Handheld; // Connect Handheld depending on Player 1's controller configuration. - if (player_index == 0 && controller_type == Settings::ControllerType::Handheld) { + if (player_index == 0) { auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; const auto handheld_connected = ui->groupConnectedController->isChecked() && controller_type == Settings::ControllerType::Handheld; @@ -877,6 +885,7 @@ void ConfigureInputPlayer::UpdateUI() { modifier_label->setVisible(!is_controller); modifier_slider->setVisible(!is_controller); range_groupbox->setVisible(is_controller); + ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); } } @@ -886,7 +895,7 @@ void ConfigureInputPlayer::SetConnectableControllers() { index_controller_type_pairs.clear(); ui->comboControllerType->clear(); - if (enable_all || npad_style_set.pro_controller == 1) { + if (enable_all || npad_style_set.fullkey == 1) { index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), Settings::ControllerType::ProController); ui->comboControllerType->addItem(tr("Pro Controller")); @@ -993,8 +1002,8 @@ void ConfigureInputPlayer::UpdateControllerIcon() { return QString{}; } }(); - - ui->controllerFrame->setStyleSheet(stylesheet.arg(theme)); + ui->controllerFrame->SetControllerType( + GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())); } void ConfigureInputPlayer::UpdateControllerAvailableButtons() { @@ -1131,7 +1140,8 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() { } void ConfigureInputPlayer::HandleClick( - QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, + QPushButton* button, std::size_t button_id, + std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type) { if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { button->setText(tr("Shake!")); @@ -1175,6 +1185,12 @@ void ConfigureInputPlayer::HandleClick( input_subsystem->GetMouseTouch()->BeginConfiguration(); } + if (type == InputCommon::Polling::DeviceType::Button) { + ui->controllerFrame->BeginMappingButton(button_id); + } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) { + ui->controllerFrame->BeginMappingAnalog(button_id); + } + timeout_timer->start(2500); // Cancel after 2.5 seconds poll_timer->start(50); // Check for new inputs every 50ms } @@ -1205,6 +1221,7 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, UpdateUI(); UpdateInputDeviceCombobox(); + ui->controllerFrame->EndMapping(); input_setter = std::nullopt; } diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index c4ae50de7..da2b89136 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -106,7 +106,7 @@ private: void LoadConfiguration(); /// Called when the button was pressed. - void HandleClick(QPushButton* button, + void HandleClick(QPushButton* button, std::size_t button_id, std::function<void(const Common::ParamPackage&)> new_input_setter, InputCommon::Polling::DeviceType type); diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 1e78b4c10..e76aa484f 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -1964,39 +1964,39 @@ </item> </layout> </item> - <item> - <widget class="QFrame" name="controllerFrame"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="styleSheet"> - <string notr="true">image: url(:/controller/pro);</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - </layout> - </widget> - </item> + <item> + <widget class="PlayerControlPreview" name="controllerFrame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="styleSheet"> + <string notr="true">image: url(:/controller/pro);</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + </layout> + </widget> + </item> <item> <layout class="QHBoxLayout" name="miscButtons"> <property name="spacing"> @@ -3087,6 +3087,14 @@ </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>PlayerControlPreview</class> + <extends>QFrame</extends> + <header>yuzu/configuration/configure_input_player_widget.h</header> + <container>1</container> + </customwidget> + </customwidgets> <resources> <include location="../../../dist/icons/controller/controller.qrc"/> </resources> diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp new file mode 100644 index 000000000..e3e8bde48 --- /dev/null +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -0,0 +1,2694 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <QMenu> +#include <QPainter> +#include <QTimer> +#include "yuzu/configuration/configure_input_player_widget.h" + +PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) { + UpdateColors(); + QTimer* timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput)); + + // refresh at 60hz + timer->start(16); +} + +PlayerControlPreview::~PlayerControlPreview() = default; + +void PlayerControlPreview::SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, + const AnalogParam& analogs_param) { + player_index = index; + Settings::ButtonsRaw buttonss; + Settings::AnalogsRaw analogs; + std::transform(buttons_param.begin(), buttons_param.end(), buttonss.begin(), + [](const Common::ParamPackage& param) { return param.Serialize(); }); + std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), + [](const Common::ParamPackage& param) { return param.Serialize(); }); + + std::transform(buttonss.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, + buttonss.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(), + Input::CreateDevice<Input::ButtonDevice>); + std::transform(analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, + analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(), + Input::CreateDevice<Input::AnalogDevice>); + UpdateColors(); +} +void PlayerControlPreview::SetPlayerInputRaw(std::size_t index, const Settings::ButtonsRaw buttons_, + Settings::AnalogsRaw analogs_) { + player_index = index; + std::transform(buttons_.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, + buttons_.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(), + Input::CreateDevice<Input::ButtonDevice>); + std::transform(analogs_.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, + analogs_.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(), + Input::CreateDevice<Input::AnalogDevice>); + UpdateColors(); +} + +PlayerControlPreview::LedPattern PlayerControlPreview::GetColorPattern(std::size_t index, + bool player_on) { + if (!player_on) { + return {0, 0, 0, 0}; + } + + switch (index) { + case 0: + return {1, 0, 0, 0}; + case 1: + return {1, 1, 0, 0}; + case 2: + return {1, 1, 1, 0}; + case 3: + return {1, 1, 1, 1}; + case 4: + return {1, 0, 0, 1}; + case 5: + return {1, 0, 1, 0}; + case 6: + return {1, 0, 1, 1}; + case 7: + return {0, 1, 1, 0}; + default: + return {0, 0, 0, 0}; + } +} + +void PlayerControlPreview::SetConnectedStatus(bool checked) { + LedPattern led_pattern = GetColorPattern(player_index, checked); + + led_color[0] = led_pattern.position1 ? colors.led_on : colors.led_off; + led_color[1] = led_pattern.position2 ? colors.led_on : colors.led_off; + led_color[2] = led_pattern.position3 ? colors.led_on : colors.led_off; + led_color[3] = led_pattern.position4 ? colors.led_on : colors.led_off; +} + +void PlayerControlPreview::SetControllerType(const Settings::ControllerType type) { + controller_type = type; + UpdateColors(); +} + +void PlayerControlPreview::BeginMappingButton(std::size_t index) { + button_mapping_index = index; + mapping_active = true; +} + +void PlayerControlPreview::BeginMappingAnalog(std::size_t index) { + button_mapping_index = Settings::NativeButton::LStick + index; + analog_mapping_index = index; + mapping_active = true; +} + +void PlayerControlPreview::EndMapping() { + button_mapping_index = Settings::NativeButton::BUTTON_NS_END; + analog_mapping_index = Settings::NativeAnalog::NumAnalogs; + mapping_active = false; + blink_counter = 0; +} + +void PlayerControlPreview::UpdateColors() { + if (QIcon::themeName().contains(QStringLiteral("dark")) || + QIcon::themeName().contains(QStringLiteral("midnight"))) { + colors.primary = QColor(204, 204, 204); + colors.button = QColor(35, 38, 41); + colors.button2 = QColor(26, 27, 30); + colors.slider_arrow = QColor(14, 15, 18); + colors.font2 = QColor(255, 255, 255); + colors.indicator = QColor(170, 238, 255); + colors.deadzone = QColor(204, 136, 136); + colors.slider_button = colors.button; + } + + if (QIcon::themeName().contains(QStringLiteral("dark"))) { + colors.outline = QColor(160, 160, 160); + } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { + colors.outline = QColor(145, 145, 145); + } else { + colors.outline = QColor(0, 0, 0); + colors.primary = QColor(225, 225, 225); + colors.button = QColor(109, 111, 114); + colors.button2 = QColor(109, 111, 114); + colors.button2 = QColor(77, 80, 84); + colors.slider_arrow = QColor(65, 68, 73); + colors.font2 = QColor(0, 0, 0); + colors.indicator = QColor(0, 0, 200); + colors.deadzone = QColor(170, 0, 0); + colors.slider_button = QColor(153, 149, 149); + } + + // Constant colors + colors.highlight = QColor(170, 0, 0); + colors.highlight2 = QColor(119, 0, 0); + colors.slider = QColor(103, 106, 110); + colors.transparent = QColor(0, 0, 0, 0); + colors.font = QColor(255, 255, 255); + colors.led_on = QColor(255, 255, 0); + colors.led_off = QColor(170, 238, 255); + + colors.left = colors.primary; + colors.right = colors.primary; + // Possible alternative to set colors from settings + // colors.left = QColor(Settings::values.players.GetValue()[player_index].body_color_left); + // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right); +} + +void PlayerControlPreview::UpdateInput() { + bool input_changed = false; + const auto& button_state = buttons; + for (std::size_t index = 0; index < button_values.size(); ++index) { + bool value = false; + if (index < Settings::NativeButton::BUTTON_NS_END) { + value = button_state[index]->GetStatus(); + } + bool blink = mapping_active && index == button_mapping_index; + if (analog_mapping_index == Settings::NativeAnalog::NUM_STICKS_HID) { + blink &= blink_counter > 25; + } + if (button_values[index] != value || blink) { + input_changed = true; + } + button_values[index] = value || blink; + } + + const auto& analog_state = sticks; + for (std::size_t index = 0; index < axis_values.size(); ++index) { + const auto [stick_x_f, stick_y_f] = analog_state[index]->GetStatus(); + const auto [stick_x_rf, stick_y_rf] = analog_state[index]->GetRawStatus(); + + if (static_cast<int>(stick_x_rf * 45) != + static_cast<int>(axis_values[index].raw_value.x() * 45) || + static_cast<int>(-stick_y_rf * 45) != + static_cast<int>(axis_values[index].raw_value.y() * 45)) { + input_changed = true; + } + + axis_values[index].properties = analog_state[index]->GetAnalogProperties(); + axis_values[index].value = QPointF(stick_x_f, -stick_y_f); + axis_values[index].raw_value = QPointF(stick_x_rf, -stick_y_rf); + + const bool blink_analog = mapping_active && index == analog_mapping_index; + if (blink_analog) { + input_changed = true; + axis_values[index].value = + QPointF(blink_counter < 25 ? -blink_counter / 25.0f : 0, + blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0); + } + } + + if (input_changed) { + update(); + } + + if (mapping_active) { + blink_counter = (blink_counter + 1) % 50; + } +} + +void PlayerControlPreview::paintEvent(QPaintEvent* event) { + QFrame::paintEvent(event); + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + const QPointF center = rect().center(); + + switch (controller_type) { + case Settings::ControllerType::Handheld: + DrawHandheldController(p, center); + break; + case Settings::ControllerType::DualJoyconDetached: + DrawDualController(p, center); + break; + case Settings::ControllerType::LeftJoycon: + DrawLeftController(p, center); + break; + case Settings::ControllerType::RightJoycon: + DrawRightController(p, center); + break; + case Settings::ControllerType::ProController: + default: + DrawProController(p, center); + break; + } +} + +void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) { + { + using namespace Settings::NativeButton; + + // Sideview left joystick + DrawJoystickSideview(p, center + QPoint(142, -69), + -axis_values[Settings::NativeAnalog::LStick].value.y(), 1.15f, + button_values[LStick]); + + // Topview D-pad buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(-163, -21), button_values[DLeft], 11, 5, Direction::Up); + DrawRoundButton(p, center + QPoint(-117, -21), button_values[DRight], 11, 5, Direction::Up); + + // Topview left joystick + DrawJoystickSideview(p, center + QPointF(-140.5f, -28), + -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1.15f, + button_values[LStick]); + + // Topview minus button + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(-111, -22), button_values[Minus], 8, 4, Direction::Up, + 1); + + // Left trigger + DrawLeftTriggers(p, center, button_values[L]); + DrawRoundButton(p, center + QPoint(151, -146), button_values[L], 8, 4, Direction::Down); + DrawLeftZTriggers(p, center, button_values[ZL]); + + // Sideview D-pad buttons + DrawRoundButton(p, center + QPoint(135, 14), button_values[DLeft], 5, 11, Direction::Right); + DrawRoundButton(p, center + QPoint(135, 36), button_values[DDown], 5, 11, Direction::Right); + DrawRoundButton(p, center + QPoint(135, -10), button_values[DUp], 5, 11, Direction::Right); + DrawRoundButton(p, center + QPoint(135, 14), button_values[DRight], 5, 11, + Direction::Right); + DrawRoundButton(p, center + QPoint(135, 71), button_values[Screenshot], 3, 8, + Direction::Right, 1); + + // Sideview minus button + DrawRoundButton(p, center + QPoint(135, -118), button_values[Minus], 4, 2.66f, + Direction::Right, 1); + + // Sideview SL and SR buttons + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(59, 52), button_values[SR], 5, 12, Direction::Left); + DrawRoundButton(p, center + QPoint(59, -69), button_values[SL], 5, 12, Direction::Left); + + DrawLeftBody(p, center); + + // Left trigger top view + DrawLeftTriggersTopView(p, center, button_values[L]); + DrawLeftZTriggersTopView(p, center, button_values[ZL]); + } + + { + // Draw joysticks + using namespace Settings::NativeAnalog; + DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f, + button_values[Settings::NativeButton::LStick]); + DrawRawJoystick(p, center + QPointF(-140, 90), axis_values[LStick].raw_value, + axis_values[LStick].properties); + } + + using namespace Settings::NativeButton; + + // D-pad constants + const QPointF dpad_center = center + QPoint(9, 14); + constexpr int dpad_distance = 23; + constexpr int dpad_radius = 11; + constexpr float dpad_arrow_size = 1.2f; + + // D-pad buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); + + // D-pad arrows + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); + + // SR and SL buttons + p.setPen(colors.outline); + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(155, 52), button_values[SR], 5.2f, 12, Direction::None, 4); + DrawRoundButton(p, center + QPoint(155, -69), button_values[SL], 5.2f, 12, Direction::None, 4); + + // SR and SL text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(155, 52), Symbol::SR, 1.0f); + DrawSymbol(p, center + QPointF(155, -69), Symbol::SL, 1.0f); + + // Minus button + button_color = colors.button; + DrawMinusButton(p, center + QPoint(39, -118), button_values[Minus], 16); + + // Screenshot button + DrawRoundButton(p, center + QPoint(26, 71), button_values[Screenshot], 8, 8); + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawCircle(p, center + QPoint(26, 71), 5); +} + +void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) { + { + using namespace Settings::NativeButton; + + // Sideview right joystick + DrawJoystickSideview(p, center + QPoint(173 - 315, 11), + axis_values[Settings::NativeAnalog::RStick].value.y() + 10.0f, 1.15f, + button_values[Settings::NativeButton::RStick]); + + // Topview face buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up); + DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up); + + // Topview right joystick + DrawJoystickSideview(p, center + QPointF(140, -28), + -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1.15f, + button_values[RStick]); + + // Topview plus button + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(111, -22), button_values[Plus], 8, 4, Direction::Up, 1); + DrawRoundButton(p, center + QPoint(111, -22), button_values[Plus], 2.66f, 4, Direction::Up, + 1); + + // Right trigger + p.setPen(colors.outline); + button_color = colors.button; + DrawRightTriggers(p, center, button_values[R]); + DrawRoundButton(p, center + QPoint(-151, -146), button_values[R], 8, 4, Direction::Down); + DrawRightZTriggers(p, center, button_values[ZR]); + + // Sideview face buttons + DrawRoundButton(p, center + QPoint(-135, -73), button_values[A], 5, 11, Direction::Left); + DrawRoundButton(p, center + QPoint(-135, -50), button_values[B], 5, 11, Direction::Left); + DrawRoundButton(p, center + QPoint(-135, -95), button_values[X], 5, 11, Direction::Left); + DrawRoundButton(p, center + QPoint(-135, -73), button_values[Y], 5, 11, Direction::Left); + + // Sideview home and plus button + DrawRoundButton(p, center + QPoint(-135, 66), button_values[Home], 3, 12, Direction::Left); + DrawRoundButton(p, center + QPoint(-135, -118), button_values[Plus], 4, 8, Direction::Left, + 1); + DrawRoundButton(p, center + QPoint(-135, -118), button_values[Plus], 4, 2.66f, + Direction::Left, 1); + + // Sideview SL and SR buttons + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(-59, 52), button_values[SL], 5, 11, Direction::Right); + DrawRoundButton(p, center + QPoint(-59, -69), button_values[SR], 5, 11, Direction::Right); + + DrawRightBody(p, center); + + // Right trigger top view + DrawRightTriggersTopView(p, center, button_values[R]); + DrawRightZTriggersTopView(p, center, button_values[ZR]); + } + + { + // Draw joysticks + using namespace Settings::NativeAnalog; + DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f, + button_values[Settings::NativeButton::RStick]); + DrawRawJoystick(p, center + QPointF(140, 90), axis_values[RStick].raw_value, + axis_values[RStick].properties); + } + + using namespace Settings::NativeButton; + + // Face buttons constants + const QPointF face_center = center + QPoint(-9, -73); + constexpr int face_distance = 23; + constexpr int face_radius = 11; + constexpr float text_size = 1.1f; + + // Face buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); + DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); + DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); + DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); + + // Face buttons text + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); + DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); + DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); + DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); + + // SR and SL buttons + p.setPen(colors.outline); + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(-155, 52), button_values[SL], 5, 12, Direction::None, 4.0f); + DrawRoundButton(p, center + QPoint(-155, -69), button_values[SR], 5, 12, Direction::None, 4.0f); + + // SR and SL text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + p.rotate(-180); + DrawSymbol(p, QPointF(-center.x(), -center.y()) + QPointF(155, 69), Symbol::SR, 1.0f); + DrawSymbol(p, QPointF(-center.x(), -center.y()) + QPointF(155, -52), Symbol::SL, 1.0f); + p.rotate(180); + + // Plus Button + DrawPlusButton(p, center + QPoint(-40, -118), button_values[Plus], 16); + + // Home Button + p.setPen(colors.outline); + button_color = colors.slider_button; + DrawCircleButton(p, center + QPoint(-26, 66), button_values[Home], 12); + button_color = colors.button; + DrawCircleButton(p, center + QPoint(-26, 66), button_values[Home], 9); + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); +} + +void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) { + { + using namespace Settings::NativeButton; + + // Left/Right trigger + DrawDualTriggers(p, center, button_values[L], button_values[R]); + + // Topview face buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up); + DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up); + + // Topview right joystick + DrawJoystickSideview(p, center + QPointF(180, -78), + -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1, + button_values[RStick]); + + // Topview plus button + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(154, -72), button_values[Plus], 7, 4, Direction::Up, 1); + DrawRoundButton(p, center + QPoint(154, -72), button_values[Plus], 2.33f, 4, Direction::Up, + 1); + + // Topview D-pad buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(-200, -71), button_values[DLeft], 10, 5, Direction::Up); + DrawRoundButton(p, center + QPoint(-160, -71), button_values[DRight], 10, 5, Direction::Up); + + // Topview left joystick + DrawJoystickSideview(p, center + QPointF(-180.5f, -78), + -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1, + button_values[LStick]); + + // Topview minus button + p.setPen(colors.outline); + button_color = colors.button; + DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up, + 1); + + DrawDualBody(p, center); + + // Right trigger top view + DrawDualTriggersTopView(p, center, button_values[L], button_values[R]); + DrawDualZTriggersTopView(p, center, button_values[ZL], button_values[ZR]); + } + + { + // Draw joysticks + using namespace Settings::NativeAnalog; + DrawJoystick(p, center + QPointF(-65, -65) + (axis_values[LStick].value * 7), 1.62f, + button_values[Settings::NativeButton::LStick]); + DrawJoystick(p, center + QPointF(65, 12) + (axis_values[RStick].value * 7), 1.62f, + button_values[Settings::NativeButton::RStick]); + DrawRawJoystick(p, center + QPointF(-180, 90), axis_values[LStick].raw_value, + axis_values[LStick].properties); + DrawRawJoystick(p, center + QPointF(180, 90), axis_values[RStick].raw_value, + axis_values[RStick].properties); + } + + using namespace Settings::NativeButton; + + // Face buttons constants + const QPointF face_center = center + QPoint(65, -65); + constexpr int face_distance = 20; + constexpr int face_radius = 10; + constexpr float text_size = 1.0f; + + // Face buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); + DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); + DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); + DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); + + // Face buttons text + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); + DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); + DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); + DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); + + // D-pad constants + const QPointF dpad_center = center + QPoint(-65, 12); + constexpr int dpad_distance = 20; + constexpr int dpad_radius = 10; + constexpr float dpad_arrow_size = 1.1f; + + // D-pad buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); + + // D-pad arrows + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); + + // Minus and Plus button + button_color = colors.button; + DrawMinusButton(p, center + QPoint(-39, -106), button_values[Minus], 14); + DrawPlusButton(p, center + QPoint(39, -106), button_values[Plus], 14); + + // Screenshot button + p.setPen(colors.outline); + DrawRoundButton(p, center + QPoint(-52, 63), button_values[Screenshot], 8, 8); + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawCircle(p, center + QPoint(-52, 63), 5); + + // Home Button + p.setPen(colors.outline); + button_color = colors.slider_button; + DrawCircleButton(p, center + QPoint(50, 60), button_values[Home], 11); + button_color = colors.button; + DrawCircleButton(p, center + QPoint(50, 60), button_values[Home], 8.5f); + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); +} + +void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) { + DrawHandheldTriggers(p, center, button_values[Settings::NativeButton::L], + button_values[Settings::NativeButton::R]); + DrawHandheldBody(p, center); + { + // Draw joysticks + using namespace Settings::NativeAnalog; + DrawJoystick(p, center + QPointF(-171, -41) + (axis_values[LStick].value * 4), 1.0f, + button_values[Settings::NativeButton::LStick]); + DrawJoystick(p, center + QPointF(171, 8) + (axis_values[RStick].value * 4), 1.0f, + button_values[Settings::NativeButton::RStick]); + DrawRawJoystick(p, center + QPointF(-50, 0), axis_values[LStick].raw_value, + axis_values[LStick].properties); + DrawRawJoystick(p, center + QPointF(50, 0), axis_values[RStick].raw_value, + axis_values[RStick].properties); + } + + using namespace Settings::NativeButton; + + // Face buttons constants + const QPointF face_center = center + QPoint(171, -41); + constexpr float face_distance = 12.8f; + constexpr float face_radius = 6.4f; + constexpr float text_size = 0.6f; + + // Face buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); + DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); + DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); + DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); + + // Face buttons text + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); + DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); + DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); + DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); + + // D-pad constants + const QPointF dpad_center = center + QPoint(-171, 8); + constexpr float dpad_distance = 12.8f; + constexpr float dpad_radius = 6.4f; + constexpr float dpad_arrow_size = 0.68f; + + // D-pad buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); + DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); + + // D-pad arrows + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); + DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); + + // ZL and ZR buttons + p.setPen(colors.outline); + DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); + DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); + DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); + + // Minus and Plus button + p.setPen(colors.outline); + button_color = colors.button; + DrawMinusButton(p, center + QPoint(-155, -67), button_values[Minus], 8); + DrawPlusButton(p, center + QPoint(155, -67), button_values[Plus], 8); + + // Screenshot button + p.setPen(colors.outline); + DrawRoundButton(p, center + QPoint(-162, 39), button_values[Screenshot], 5, 5); + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawCircle(p, center + QPoint(-162, 39), 3); + + // Home Button + p.setPen(colors.outline); + button_color = colors.slider_button; + DrawCircleButton(p, center + QPoint(161, 37), button_values[Home], 7); + button_color = colors.button; + DrawCircleButton(p, center + QPoint(161, 37), button_values[Home], 5); + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); +} + +void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) { + DrawProTriggers(p, center, button_values[Settings::NativeButton::L], + button_values[Settings::NativeButton::R]); + DrawProBody(p, center); + { + // Draw joysticks + using namespace Settings::NativeAnalog; + DrawProJoystick(p, center + QPointF(-111, -55) + (axis_values[LStick].value * 11), + button_values[Settings::NativeButton::LStick]); + DrawProJoystick(p, center + QPointF(51, 0) + (axis_values[RStick].value * 11), + button_values[Settings::NativeButton::RStick]); + DrawRawJoystick(p, center + QPointF(-50, 105), axis_values[LStick].raw_value, + axis_values[LStick].properties); + DrawRawJoystick(p, center + QPointF(50, 105), axis_values[RStick].raw_value, + axis_values[RStick].properties); + } + + using namespace Settings::NativeButton; + + // Face buttons constants + const QPointF face_center = center + QPoint(105, -56); + constexpr int face_distance = 31; + constexpr int face_radius = 15; + constexpr float text_size = 1.5f; + + // Face buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); + DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); + DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); + DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); + + // Face buttons text + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); + DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); + DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); + DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); + + // D-pad buttons + const QPointF dpad_postion = center + QPoint(-61, 0); + DrawArrowButton(p, dpad_postion, Direction::Up, button_values[DUp]); + DrawArrowButton(p, dpad_postion, Direction::Left, button_values[DLeft]); + DrawArrowButton(p, dpad_postion, Direction::Right, button_values[DRight]); + DrawArrowButton(p, dpad_postion, Direction::Down, button_values[DDown]); + DrawArrowButtonOutline(p, dpad_postion); + + // ZL and ZR buttons + p.setPen(colors.outline); + DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); + DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); + DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); + + // Minus and Plus buttons + p.setPen(colors.outline); + DrawCircleButton(p, center + QPoint(-50, -86), button_values[Minus], 9); + DrawCircleButton(p, center + QPoint(50, -86), button_values[Plus], 9); + + // Minus and Plus symbols + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawRectangle(p, center + QPoint(-50, -86), 9, 1.5f); + DrawRectangle(p, center + QPoint(50, -86), 9, 1.5f); + DrawRectangle(p, center + QPoint(50, -86), 1.5f, 9); + + // Screenshot button + p.setPen(colors.outline); + DrawRoundButton(p, center + QPoint(-29, -56), button_values[Screenshot], 7, 7); + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawCircle(p, center + QPoint(-29, -56), 4.5f); + + // Home Button + p.setPen(colors.outline); + button_color = colors.slider_button; + DrawCircleButton(p, center + QPoint(29, -56), button_values[Home], 10.0f); + button_color = colors.button; + DrawCircleButton(p, center + QPoint(29, -56), button_values[Home], 7.1f); + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); +} + +void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) { + DrawGCTriggers(p, center, button_values[Settings::NativeButton::ZL], + button_values[Settings::NativeButton::ZR]); + DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]); + DrawGCBody(p, center); + { + // Draw joysticks + using namespace Settings::NativeAnalog; + DrawGCJoystick(p, center + QPointF(-111, -44) + (axis_values[LStick].value * 10), false); + button_color = colors.button2; + DrawCircleButton(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), false, + 15); + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C, + 1.0f); + DrawRawJoystick(p, center + QPointF(-198, -125), axis_values[LStick].raw_value, + axis_values[LStick].properties); + DrawRawJoystick(p, center + QPointF(198, -125), axis_values[RStick].raw_value, + axis_values[RStick].properties); + } + + using namespace Settings::NativeButton; + + // Face buttons constants + constexpr float text_size = 1.1f; + + // Face buttons + p.setPen(colors.outline); + button_color = colors.button; + DrawCircleButton(p, center + QPoint(111, -44), button_values[A], 21); + DrawCircleButton(p, center + QPoint(70, -23), button_values[B], 13); + DrawGCButtonX(p, center, button_values[Settings::NativeButton::X]); + DrawGCButtonY(p, center, button_values[Settings::NativeButton::Y]); + + // Face buttons text + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, center + QPoint(111, -44), Symbol::A, 1.5f); + DrawSymbol(p, center + QPoint(70, -23), Symbol::B, text_size); + DrawSymbol(p, center + QPoint(151, -53), Symbol::X, text_size); + DrawSymbol(p, center + QPoint(100, -83), Symbol::Y, text_size); + + // D-pad buttons + const QPointF dpad_postion = center + QPoint(-61, 37); + const float dpad_size = 0.8f; + DrawArrowButton(p, dpad_postion, Direction::Up, button_values[DUp], dpad_size); + DrawArrowButton(p, dpad_postion, Direction::Left, button_values[DLeft], dpad_size); + DrawArrowButton(p, dpad_postion, Direction::Right, button_values[DRight], dpad_size); + DrawArrowButton(p, dpad_postion, Direction::Down, button_values[DDown], dpad_size); + DrawArrowButtonOutline(p, dpad_postion, dpad_size); + + // Minus and Plus buttons + p.setPen(colors.outline); + DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); +} + +constexpr std::array<float, 13 * 2> symbol_a = { + -1.085f, -5.2f, 1.085f, -5.2f, 5.085f, 5.0f, 2.785f, 5.0f, 1.785f, + 2.65f, -1.785f, 2.65f, -2.785f, 5.0f, -5.085f, 5.0f, -1.4f, 1.0f, + 0.0f, -2.8f, 1.4f, 1.0f, -1.4f, 1.0f, -5.085f, 5.0f, +}; +constexpr std::array<float, 134 * 2> symbol_b = { + -4.0f, 0.0f, -4.0f, 0.0f, -4.0f, -0.1f, -3.8f, -5.1f, 1.8f, -5.0f, 2.3f, -4.9f, 2.6f, + -4.8f, 2.8f, -4.7f, 2.9f, -4.6f, 3.1f, -4.5f, 3.2f, -4.4f, 3.4f, -4.3f, 3.4f, -4.2f, + 3.5f, -4.1f, 3.7f, -4.0f, 3.7f, -3.9f, 3.8f, -3.8f, 3.8f, -3.7f, 3.9f, -3.6f, 3.9f, + -3.5f, 4.0f, -3.4f, 4.0f, -3.3f, 4.1f, -3.1f, 4.1f, -3.0f, 4.0f, -2.0f, 4.0f, -1.9f, + 3.9f, -1.7f, 3.9f, -1.6f, 3.8f, -1.5f, 3.8f, -1.4f, 3.7f, -1.3f, 3.7f, -1.2f, 3.6f, + -1.1f, 3.6f, -1.0f, 3.5f, -0.9f, 3.3f, -0.8f, 3.3f, -0.7f, 3.2f, -0.6f, 3.0f, -0.5f, + 2.9f, -0.4f, 2.7f, -0.3f, 2.9f, -0.2f, 3.2f, -0.1f, 3.3f, 0.0f, 3.5f, 0.1f, 3.6f, + 0.2f, 3.8f, 0.3f, 3.9f, 0.4f, 4.0f, 0.6f, 4.1f, 0.7f, 4.3f, 0.8f, 4.3f, 0.9f, + 4.4f, 1.0f, 4.4f, 1.1f, 4.5f, 1.3f, 4.5f, 1.4f, 4.6f, 1.6f, 4.6f, 1.7f, 4.5f, + 2.8f, 4.5f, 2.9f, 4.4f, 3.1f, 4.4f, 3.2f, 4.3f, 3.4f, 4.3f, 3.5f, 4.2f, 3.6f, + 4.2f, 3.7f, 4.1f, 3.8f, 4.1f, 3.9f, 4.0f, 4.0f, 3.9f, 4.2f, 3.8f, 4.3f, 3.6f, + 4.4f, 3.6f, 4.5f, 3.4f, 4.6f, 3.3f, 4.7f, 3.1f, 4.8f, 2.8f, 4.9f, 2.6f, 5.0f, + 2.1f, 5.1f, -4.0f, 5.0f, -4.0f, 4.9f, + + -4.0f, 0.0f, 1.1f, 3.4f, 1.1f, 3.4f, 1.5f, 3.3f, 1.8f, 3.2f, 2.0f, 3.1f, 2.1f, + 3.0f, 2.3f, 2.9f, 2.3f, 2.8f, 2.4f, 2.7f, 2.4f, 2.6f, 2.5f, 2.3f, 2.5f, 2.2f, + 2.4f, 1.7f, 2.4f, 1.6f, 2.3f, 1.4f, 2.3f, 1.3f, 2.2f, 1.2f, 2.2f, 1.1f, 2.1f, + 1.0f, 1.9f, 0.9f, 1.6f, 0.8f, 1.4f, 0.7f, -1.9f, 0.6f, -1.9f, 0.7f, -1.8f, 3.4f, + 1.1f, 3.4f, -4.0f, 0.0f, + + 0.3f, -1.1f, 0.3f, -1.1f, 1.3f, -1.2f, 1.5f, -1.3f, 1.8f, -1.4f, 1.8f, -1.5f, 1.9f, + -1.6f, 2.0f, -1.8f, 2.0f, -1.9f, 2.1f, -2.0f, 2.1f, -2.1f, 2.0f, -2.7f, 2.0f, -2.8f, + 1.9f, -2.9f, 1.9f, -3.0f, 1.8f, -3.1f, 1.6f, -3.2f, 1.6f, -3.3f, 1.3f, -3.4f, -1.9f, + -3.3f, -1.9f, -3.2f, -1.8f, -1.0f, 0.2f, -1.1f, 0.3f, -1.1f, -4.0f, 0.0f, +}; + +constexpr std::array<float, 9 * 2> symbol_y = { + -4.79f, -4.9f, -2.44f, -4.9f, 0.0f, -0.9f, 2.44f, -4.9f, 4.79f, + -4.9f, 1.05f, 1.0f, 1.05f, 5.31f, -1.05f, 5.31f, -1.05f, 1.0f, + +}; + +constexpr std::array<float, 12 * 2> symbol_x = { + -4.4f, -5.0f, -2.0f, -5.0f, 0.0f, -1.7f, 2.0f, -5.0f, 4.4f, -5.0f, 1.2f, 0.0f, + 4.4f, 5.0f, 2.0f, 5.0f, 0.0f, 1.7f, -2.0f, 5.0f, -4.4f, 5.0f, -1.2f, 0.0f, + +}; + +constexpr std::array<float, 7 * 2> symbol_l = { + 2.4f, -3.23f, 2.4f, 2.1f, 5.43f, 2.1f, 5.43f, 3.22f, 0.98f, 3.22f, 0.98f, -3.23f, 2.4f, -3.23f, +}; + +constexpr std::array<float, 98 * 2> symbol_r = { + 1.0f, 0.0f, 1.0f, -0.1f, 1.1f, -3.3f, 4.3f, -3.2f, 5.1f, -3.1f, 5.4f, -3.0f, 5.6f, -2.9f, + 5.7f, -2.8f, 5.9f, -2.7f, 5.9f, -2.6f, 6.0f, -2.5f, 6.1f, -2.3f, 6.2f, -2.2f, 6.2f, -2.1f, + 6.3f, -2.0f, 6.3f, -1.9f, 6.2f, -0.8f, 6.2f, -0.7f, 6.1f, -0.6f, 6.1f, -0.5f, 6.0f, -0.4f, + 6.0f, -0.3f, 5.9f, -0.2f, 5.7f, -0.1f, 5.7f, 0.0f, 5.6f, 0.1f, 5.4f, 0.2f, 5.1f, 0.3f, + 4.7f, 0.4f, 4.7f, 0.5f, 4.9f, 0.6f, 5.0f, 0.7f, 5.2f, 0.8f, 5.2f, 0.9f, 5.3f, 1.0f, + 5.5f, 1.1f, 5.5f, 1.2f, 5.6f, 1.3f, 5.7f, 1.5f, 5.8f, 1.6f, 5.9f, 1.8f, 6.0f, 1.9f, + 6.1f, 2.1f, 6.2f, 2.2f, 6.2f, 2.3f, 6.3f, 2.4f, 6.4f, 2.6f, 6.5f, 2.7f, 6.6f, 2.9f, + 6.7f, 3.0f, 6.7f, 3.1f, 6.8f, 3.2f, 6.8f, 3.3f, 5.3f, 3.2f, 5.2f, 3.1f, 5.2f, 3.0f, + 5.1f, 2.9f, 5.0f, 2.7f, 4.9f, 2.6f, 4.8f, 2.4f, 4.7f, 2.3f, 4.6f, 2.1f, 4.5f, 2.0f, + 4.4f, 1.8f, 4.3f, 1.7f, 4.1f, 1.4f, 4.0f, 1.3f, 3.9f, 1.1f, 3.8f, 1.0f, 3.6f, 0.9f, + 3.6f, 0.8f, 3.5f, 0.7f, 3.3f, 0.6f, 2.9f, 0.5f, 2.3f, 0.6f, 2.3f, 0.7f, 2.2f, 3.3f, + 1.0f, 3.2f, 1.0f, 3.1f, 1.0f, 0.0f, + + 4.2f, -0.5f, 4.4f, -0.6f, 4.7f, -0.7f, 4.8f, -0.8f, 4.9f, -1.0f, 5.0f, -1.1f, 5.0f, -1.2f, + 4.9f, -1.7f, 4.9f, -1.8f, 4.8f, -1.9f, 4.8f, -2.0f, 4.6f, -2.1f, 4.3f, -2.2f, 2.3f, -2.1f, + 2.3f, -2.0f, 2.4f, -0.5f, 4.2f, -0.5f, 1.0f, 0.0f, +}; + +constexpr std::array<float, 18 * 2> symbol_zl = { + -2.6f, -2.13f, -5.6f, -2.13f, -5.6f, -3.23f, -0.8f, -3.23f, -0.8f, -2.13f, -4.4f, 2.12f, + -0.7f, 2.12f, -0.7f, 3.22f, -6.0f, 3.22f, -6.0f, 2.12f, 2.4f, -3.23f, 2.4f, 2.1f, + 5.43f, 2.1f, 5.43f, 3.22f, 0.98f, 3.22f, 0.98f, -3.23f, 2.4f, -3.23f, -6.0f, 2.12f, +}; + +constexpr std::array<float, 57 * 2> symbol_sl = { + -3.0f, -3.65f, -2.76f, -4.26f, -2.33f, -4.76f, -1.76f, -5.09f, -1.13f, -5.26f, -0.94f, + -4.77f, -0.87f, -4.11f, -1.46f, -3.88f, -1.91f, -3.41f, -2.05f, -2.78f, -1.98f, -2.13f, + -1.59f, -1.61f, -0.96f, -1.53f, -0.56f, -2.04f, -0.38f, -2.67f, -0.22f, -3.31f, 0.0f, + -3.93f, 0.34f, -4.49f, 0.86f, -4.89f, 1.49f, -5.05f, 2.14f, -4.95f, 2.69f, -4.6f, + 3.07f, -4.07f, 3.25f, -3.44f, 3.31f, -2.78f, 3.25f, -2.12f, 3.07f, -1.49f, 2.7f, + -0.95f, 2.16f, -0.58f, 1.52f, -0.43f, 1.41f, -0.99f, 1.38f, -1.65f, 1.97f, -1.91f, + 2.25f, -2.49f, 2.25f, -3.15f, 1.99f, -3.74f, 1.38f, -3.78f, 1.06f, -3.22f, 0.88f, + -2.58f, 0.71f, -1.94f, 0.49f, -1.32f, 0.13f, -0.77f, -0.4f, -0.4f, -1.04f, -0.25f, + -1.69f, -0.32f, -2.28f, -0.61f, -2.73f, -1.09f, -2.98f, -1.69f, -3.09f, -2.34f, + + 3.23f, 2.4f, -2.1f, 2.4f, -2.1f, 5.43f, -3.22f, 5.43f, -3.22f, 0.98f, 3.23f, + 0.98f, 3.23f, 2.4f, -3.09f, -2.34f, +}; +constexpr std::array<float, 109 * 2> symbol_zr = { + -2.6f, -2.13f, -5.6f, -2.13f, -5.6f, -3.23f, -0.8f, -3.23f, -0.8f, -2.13f, -4.4f, 2.12f, -0.7f, + 2.12f, -0.7f, 3.22f, -6.0f, 3.22f, -6.0f, 2.12f, + + 1.0f, 0.0f, 1.0f, -0.1f, 1.1f, -3.3f, 4.3f, -3.2f, 5.1f, -3.1f, 5.4f, -3.0f, 5.6f, + -2.9f, 5.7f, -2.8f, 5.9f, -2.7f, 5.9f, -2.6f, 6.0f, -2.5f, 6.1f, -2.3f, 6.2f, -2.2f, + 6.2f, -2.1f, 6.3f, -2.0f, 6.3f, -1.9f, 6.2f, -0.8f, 6.2f, -0.7f, 6.1f, -0.6f, 6.1f, + -0.5f, 6.0f, -0.4f, 6.0f, -0.3f, 5.9f, -0.2f, 5.7f, -0.1f, 5.7f, 0.0f, 5.6f, 0.1f, + 5.4f, 0.2f, 5.1f, 0.3f, 4.7f, 0.4f, 4.7f, 0.5f, 4.9f, 0.6f, 5.0f, 0.7f, 5.2f, + 0.8f, 5.2f, 0.9f, 5.3f, 1.0f, 5.5f, 1.1f, 5.5f, 1.2f, 5.6f, 1.3f, 5.7f, 1.5f, + 5.8f, 1.6f, 5.9f, 1.8f, 6.0f, 1.9f, 6.1f, 2.1f, 6.2f, 2.2f, 6.2f, 2.3f, 6.3f, + 2.4f, 6.4f, 2.6f, 6.5f, 2.7f, 6.6f, 2.9f, 6.7f, 3.0f, 6.7f, 3.1f, 6.8f, 3.2f, + 6.8f, 3.3f, 5.3f, 3.2f, 5.2f, 3.1f, 5.2f, 3.0f, 5.1f, 2.9f, 5.0f, 2.7f, 4.9f, + 2.6f, 4.8f, 2.4f, 4.7f, 2.3f, 4.6f, 2.1f, 4.5f, 2.0f, 4.4f, 1.8f, 4.3f, 1.7f, + 4.1f, 1.4f, 4.0f, 1.3f, 3.9f, 1.1f, 3.8f, 1.0f, 3.6f, 0.9f, 3.6f, 0.8f, 3.5f, + 0.7f, 3.3f, 0.6f, 2.9f, 0.5f, 2.3f, 0.6f, 2.3f, 0.7f, 2.2f, 3.3f, 1.0f, 3.2f, + 1.0f, 3.1f, 1.0f, 0.0f, + + 4.2f, -0.5f, 4.4f, -0.6f, 4.7f, -0.7f, 4.8f, -0.8f, 4.9f, -1.0f, 5.0f, -1.1f, 5.0f, + -1.2f, 4.9f, -1.7f, 4.9f, -1.8f, 4.8f, -1.9f, 4.8f, -2.0f, 4.6f, -2.1f, 4.3f, -2.2f, + 2.3f, -2.1f, 2.3f, -2.0f, 2.4f, -0.5f, 4.2f, -0.5f, 1.0f, 0.0f, -6.0f, 2.12f, +}; + +constexpr std::array<float, 148 * 2> symbol_sr = { + -3.0f, -3.65f, -2.76f, -4.26f, -2.33f, -4.76f, -1.76f, -5.09f, -1.13f, -5.26f, -0.94f, -4.77f, + -0.87f, -4.11f, -1.46f, -3.88f, -1.91f, -3.41f, -2.05f, -2.78f, -1.98f, -2.13f, -1.59f, -1.61f, + -0.96f, -1.53f, -0.56f, -2.04f, -0.38f, -2.67f, -0.22f, -3.31f, 0.0f, -3.93f, 0.34f, -4.49f, + 0.86f, -4.89f, 1.49f, -5.05f, 2.14f, -4.95f, 2.69f, -4.6f, 3.07f, -4.07f, 3.25f, -3.44f, + 3.31f, -2.78f, 3.25f, -2.12f, 3.07f, -1.49f, 2.7f, -0.95f, 2.16f, -0.58f, 1.52f, -0.43f, + 1.41f, -0.99f, 1.38f, -1.65f, 1.97f, -1.91f, 2.25f, -2.49f, 2.25f, -3.15f, 1.99f, -3.74f, + 1.38f, -3.78f, 1.06f, -3.22f, 0.88f, -2.58f, 0.71f, -1.94f, 0.49f, -1.32f, 0.13f, -0.77f, + -0.4f, -0.4f, -1.04f, -0.25f, -1.69f, -0.32f, -2.28f, -0.61f, -2.73f, -1.09f, -2.98f, -1.69f, + -3.09f, -2.34f, + + -1.0f, 0.0f, 0.1f, 1.0f, 3.3f, 1.1f, 3.2f, 4.3f, 3.1f, 5.1f, 3.0f, 5.4f, + 2.9f, 5.6f, 2.8f, 5.7f, 2.7f, 5.9f, 2.6f, 5.9f, 2.5f, 6.0f, 2.3f, 6.1f, + 2.2f, 6.2f, 2.1f, 6.2f, 2.0f, 6.3f, 1.9f, 6.3f, 0.8f, 6.2f, 0.7f, 6.2f, + 0.6f, 6.1f, 0.5f, 6.1f, 0.4f, 6.0f, 0.3f, 6.0f, 0.2f, 5.9f, 0.1f, 5.7f, + 0.0f, 5.7f, -0.1f, 5.6f, -0.2f, 5.4f, -0.3f, 5.1f, -0.4f, 4.7f, -0.5f, 4.7f, + -0.6f, 4.9f, -0.7f, 5.0f, -0.8f, 5.2f, -0.9f, 5.2f, -1.0f, 5.3f, -1.1f, 5.5f, + -1.2f, 5.5f, -1.3f, 5.6f, -1.5f, 5.7f, -1.6f, 5.8f, -1.8f, 5.9f, -1.9f, 6.0f, + -2.1f, 6.1f, -2.2f, 6.2f, -2.3f, 6.2f, -2.4f, 6.3f, -2.6f, 6.4f, -2.7f, 6.5f, + -2.9f, 6.6f, -3.0f, 6.7f, -3.1f, 6.7f, -3.2f, 6.8f, -3.3f, 6.8f, -3.2f, 5.3f, + -3.1f, 5.2f, -3.0f, 5.2f, -2.9f, 5.1f, -2.7f, 5.0f, -2.6f, 4.9f, -2.4f, 4.8f, + -2.3f, 4.7f, -2.1f, 4.6f, -2.0f, 4.5f, -1.8f, 4.4f, -1.7f, 4.3f, -1.4f, 4.1f, + -1.3f, 4.0f, -1.1f, 3.9f, -1.0f, 3.8f, -0.9f, 3.6f, -0.8f, 3.6f, -0.7f, 3.5f, + -0.6f, 3.3f, -0.5f, 2.9f, -0.6f, 2.3f, -0.7f, 2.3f, -3.3f, 2.2f, -3.2f, 1.0f, + -3.1f, 1.0f, 0.0f, 1.0f, + + 0.5f, 4.2f, 0.6f, 4.4f, 0.7f, 4.7f, 0.8f, 4.8f, 1.0f, 4.9f, 1.1f, 5.0f, + 1.2f, 5.0f, 1.7f, 4.9f, 1.8f, 4.9f, 1.9f, 4.8f, 2.0f, 4.8f, 2.1f, 4.6f, + 2.2f, 4.3f, 2.1f, 2.3f, 2.0f, 2.3f, 0.5f, 2.4f, 0.5f, 4.2f, -0.0f, 1.0f, + -3.09f, -2.34f, + +}; + +constexpr std::array<float, 30 * 2> symbol_c = { + 2.86f, 7.57f, 0.99f, 7.94f, -0.91f, 7.87f, -2.73f, 7.31f, -4.23f, 6.14f, -5.2f, 4.51f, + -5.65f, 2.66f, -5.68f, 0.75f, -5.31f, -1.12f, -4.43f, -2.81f, -3.01f, -4.08f, -1.24f, -4.78f, + 0.66f, -4.94f, 2.54f, -4.67f, 4.33f, -4.0f, 4.63f, -2.27f, 3.37f, -2.7f, 1.6f, -3.4f, + -0.3f, -3.5f, -2.09f, -2.87f, -3.34f, -1.45f, -3.91f, 0.37f, -3.95f, 2.27f, -3.49f, 4.12f, + -2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f, +}; + +constexpr std::array<float, 12 * 2> house = { + -1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f, + 0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f, +}; + +constexpr std::array<float, 11 * 2> up_arrow_button = { + 9.1f, -9.1f, 9.1f, -30.0f, 8.1f, -30.1f, 7.7f, -30.1f, -8.6f, -30.0f, -9.0f, + -29.8f, -9.3f, -29.5f, -9.5f, -29.1f, -9.1f, -28.7f, -9.1f, -9.1f, 0.0f, 0.6f, +}; + +constexpr std::array<float, 3 * 2> up_arrow_symbol = { + 0.0f, -3.0f, -3.0f, 2.0f, 3.0f, 2.0f, +}; + +constexpr std::array<float, 13 * 2> up_arrow = { + 9.4f, -9.8f, 9.4f, -10.2f, 8.9f, -29.8f, 8.5f, -30.0f, 8.1f, + -30.1f, 7.7f, -30.1f, -8.6f, -30.0f, -9.0f, -29.8f, -9.3f, -29.5f, + -9.5f, -29.1f, -9.5f, -28.7f, -9.1f, -9.1f, -8.8f, -8.8f, +}; + +constexpr std::array<float, 64 * 2> trigger_button = { + 5.5f, -12.6f, 5.8f, -12.6f, 6.7f, -12.5f, 8.1f, -12.3f, 8.6f, -12.2f, 9.2f, -12.0f, + 9.5f, -11.9f, 9.9f, -11.8f, 10.6f, -11.5f, 11.0f, -11.3f, 11.2f, -11.2f, 11.4f, -11.1f, + 11.8f, -10.9f, 12.0f, -10.8f, 12.2f, -10.7f, 12.4f, -10.5f, 12.6f, -10.4f, 12.8f, -10.3f, + 13.6f, -9.7f, 13.8f, -9.6f, 13.9f, -9.4f, 14.1f, -9.3f, 14.8f, -8.6f, 15.0f, -8.5f, + 15.1f, -8.3f, 15.6f, -7.8f, 15.7f, -7.6f, 16.1f, -7.0f, 16.3f, -6.8f, 16.4f, -6.6f, + 16.5f, -6.4f, 16.8f, -6.0f, 16.9f, -5.8f, 17.0f, -5.6f, 17.1f, -5.4f, 17.2f, -5.2f, + 17.3f, -5.0f, 17.4f, -4.8f, 17.5f, -4.6f, 17.6f, -4.4f, 17.7f, -4.1f, 17.8f, -3.9f, + 17.9f, -3.5f, 18.0f, -3.3f, 18.1f, -3.0f, 18.2f, -2.6f, 18.2f, -2.3f, 18.3f, -2.1f, + 18.3f, -1.9f, 18.4f, -1.4f, 18.5f, -1.2f, 18.6f, -0.3f, 18.6f, 0.0f, 18.3f, 13.9f, + -17.0f, 13.8f, -17.0f, 13.6f, -16.4f, -11.4f, -16.3f, -11.6f, -16.1f, -11.8f, -15.7f, -12.0f, + -15.5f, -12.1f, -15.1f, -12.3f, -14.6f, -12.4f, -13.4f, -12.5f, +}; + +constexpr std::array<float, 36 * 2> pro_left_trigger = { + -65.2f, -132.6f, -68.2f, -134.1f, -71.3f, -135.5f, -74.4f, -136.7f, -77.6f, + -137.6f, -80.9f, -138.1f, -84.3f, -138.3f, -87.6f, -138.3f, -91.0f, -138.1f, + -94.3f, -137.8f, -97.6f, -137.3f, -100.9f, -136.7f, -107.5f, -135.3f, -110.7f, + -134.5f, -120.4f, -131.8f, -123.6f, -130.8f, -126.8f, -129.7f, -129.9f, -128.5f, + -132.9f, -127.1f, -135.9f, -125.6f, -138.8f, -123.9f, -141.6f, -122.0f, -144.1f, + -119.8f, -146.3f, -117.3f, -148.4f, -114.7f, -150.4f, -112.0f, -152.3f, -109.2f, + -155.3f, -104.0f, -152.0f, -104.3f, -148.7f, -104.5f, -145.3f, -104.8f, -35.5f, + -117.2f, -38.5f, -118.7f, -41.4f, -120.3f, -44.4f, -121.8f, -50.4f, -124.9f, +}; + +constexpr std::array<float, 14 * 2> pro_body_top = { + 0.0f, -115.4f, -4.4f, -116.1f, -69.7f, -131.3f, -66.4f, -131.9f, -63.1f, -132.3f, + -56.4f, -133.0f, -53.1f, -133.3f, -49.8f, -133.5f, -43.1f, -133.8f, -39.8f, -134.0f, + -36.5f, -134.1f, -16.4f, -134.4f, -13.1f, -134.4f, 0.0f, -134.1f, +}; + +constexpr std::array<float, 145 * 2> pro_left_handle = { + -178.7f, -47.5f, -179.0f, -46.1f, -179.3f, -44.6f, -182.0f, -29.8f, -182.3f, -28.4f, + -182.6f, -26.9f, -182.8f, -25.4f, -183.1f, -23.9f, -183.3f, -22.4f, -183.6f, -21.0f, + -183.8f, -19.5f, -184.1f, -18.0f, -184.3f, -16.5f, -184.6f, -15.1f, -184.8f, -13.6f, + -185.1f, -12.1f, -185.3f, -10.6f, -185.6f, -9.1f, -185.8f, -7.7f, -186.1f, -6.2f, + -186.3f, -4.7f, -186.6f, -3.2f, -186.8f, -1.7f, -187.1f, -0.3f, -187.3f, 1.2f, + -187.6f, 2.7f, -187.8f, 4.2f, -188.3f, 7.1f, -188.5f, 8.6f, -188.8f, 10.1f, + -189.0f, 11.6f, -189.3f, 13.1f, -189.5f, 14.5f, -190.0f, 17.5f, -190.2f, 19.0f, + -190.5f, 20.5f, -190.7f, 21.9f, -191.2f, 24.9f, -191.4f, 26.4f, -191.7f, 27.9f, + -191.9f, 29.3f, -192.4f, 32.3f, -192.6f, 33.8f, -193.1f, 36.8f, -193.3f, 38.2f, + -193.8f, 41.2f, -194.0f, 42.7f, -194.7f, 47.1f, -194.9f, 48.6f, -199.0f, 82.9f, + -199.1f, 84.4f, -199.1f, 85.9f, -199.2f, 87.4f, -199.2f, 88.9f, -199.1f, 94.9f, + -198.9f, 96.4f, -198.8f, 97.8f, -198.5f, 99.3f, -198.3f, 100.8f, -198.0f, 102.3f, + -197.7f, 103.7f, -197.4f, 105.2f, -197.0f, 106.7f, -196.6f, 108.1f, -195.7f, 111.0f, + -195.2f, 112.4f, -194.1f, 115.2f, -193.5f, 116.5f, -192.8f, 117.9f, -192.1f, 119.2f, + -190.6f, 121.8f, -189.8f, 123.1f, -188.9f, 124.3f, -187.0f, 126.6f, -186.0f, 127.7f, + -183.9f, 129.8f, -182.7f, 130.8f, -180.3f, 132.6f, -179.1f, 133.4f, -177.8f, 134.1f, + -176.4f, 134.8f, -175.1f, 135.5f, -173.7f, 136.0f, -169.4f, 137.3f, -167.9f, 137.7f, + -166.5f, 138.0f, -165.0f, 138.3f, -163.5f, 138.4f, -162.0f, 138.4f, -160.5f, 138.3f, + -159.0f, 138.0f, -157.6f, 137.7f, -156.1f, 137.3f, -154.7f, 136.9f, -153.2f, 136.5f, + -151.8f, 136.0f, -150.4f, 135.4f, -149.1f, 134.8f, -147.7f, 134.1f, -146.5f, 133.3f, + -145.2f, 132.5f, -144.0f, 131.6f, -142.8f, 130.6f, -141.7f, 129.6f, -139.6f, 127.5f, + -138.6f, 126.4f, -137.7f, 125.2f, -135.1f, 121.5f, -134.3f, 120.3f, -133.5f, 119.0f, + -131.9f, 116.5f, -131.1f, 115.2f, -128.8f, 111.3f, -128.0f, 110.1f, -127.2f, 108.8f, + -126.5f, 107.5f, -125.7f, 106.2f, -125.0f, 104.9f, -124.2f, 103.6f, -123.5f, 102.3f, + -122.0f, 99.6f, -121.3f, 98.3f, -115.8f, 87.7f, -115.1f, 86.4f, -114.4f, 85.0f, + -113.7f, 83.7f, -112.3f, 81.0f, -111.6f, 79.7f, -110.1f, 77.1f, -109.4f, 75.8f, + -108.0f, 73.1f, -107.2f, 71.8f, -106.4f, 70.6f, -105.7f, 69.3f, -104.8f, 68.0f, + -104.0f, 66.8f, -103.1f, 65.6f, -101.1f, 63.3f, -100.0f, 62.3f, -98.8f, 61.4f, + -97.6f, 60.6f, -97.9f, 59.5f, -98.8f, 58.3f, -101.5f, 54.6f, -102.4f, 53.4f, +}; + +constexpr std::array<float, 245 * 2> pro_body = { + -0.7f, -129.1f, -54.3f, -129.1f, -55.0f, -129.1f, -57.8f, -129.0f, -58.5f, -129.0f, + -60.7f, -128.9f, -61.4f, -128.9f, -62.8f, -128.8f, -63.5f, -128.8f, -65.7f, -128.7f, + -66.4f, -128.7f, -67.8f, -128.6f, -68.5f, -128.6f, -69.2f, -128.5f, -70.0f, -128.5f, + -70.7f, -128.4f, -71.4f, -128.4f, -72.1f, -128.3f, -72.8f, -128.3f, -73.5f, -128.2f, + -74.2f, -128.2f, -74.9f, -128.1f, -75.7f, -128.1f, -76.4f, -128.0f, -77.1f, -128.0f, + -77.8f, -127.9f, -78.5f, -127.9f, -79.2f, -127.8f, -80.6f, -127.7f, -81.4f, -127.6f, + -82.1f, -127.5f, -82.8f, -127.5f, -83.5f, -127.4f, -84.9f, -127.3f, -85.6f, -127.2f, + -87.0f, -127.1f, -87.7f, -127.0f, -88.5f, -126.9f, -89.2f, -126.8f, -89.9f, -126.8f, + -90.6f, -126.7f, -94.1f, -126.3f, -94.8f, -126.2f, -113.2f, -123.3f, -113.9f, -123.2f, + -114.6f, -123.0f, -115.3f, -122.9f, -116.7f, -122.6f, -117.4f, -122.5f, -118.1f, -122.3f, + -118.8f, -122.2f, -119.5f, -122.0f, -120.9f, -121.7f, -121.6f, -121.5f, -122.3f, -121.4f, + -122.9f, -121.2f, -123.6f, -121.0f, -126.4f, -120.3f, -127.1f, -120.1f, -127.8f, -119.8f, + -128.4f, -119.6f, -129.1f, -119.4f, -131.2f, -118.7f, -132.5f, -118.3f, -133.2f, -118.0f, + -133.8f, -117.7f, -134.5f, -117.4f, -135.1f, -117.2f, -135.8f, -116.9f, -136.4f, -116.5f, + -137.0f, -116.2f, -137.7f, -115.8f, -138.3f, -115.4f, -138.9f, -115.1f, -139.5f, -114.7f, + -160.0f, -100.5f, -160.5f, -100.0f, -162.5f, -97.9f, -162.9f, -97.4f, -163.4f, -96.8f, + -163.8f, -96.2f, -165.3f, -93.8f, -165.7f, -93.2f, -166.0f, -92.6f, -166.4f, -91.9f, + -166.7f, -91.3f, -167.3f, -90.0f, -167.6f, -89.4f, -167.8f, -88.7f, -168.1f, -88.0f, + -168.4f, -87.4f, -168.6f, -86.7f, -168.9f, -86.0f, -169.1f, -85.4f, -169.3f, -84.7f, + -169.6f, -84.0f, -169.8f, -83.3f, -170.2f, -82.0f, -170.4f, -81.3f, -172.8f, -72.3f, + -173.0f, -71.6f, -173.5f, -69.5f, -173.7f, -68.8f, -173.9f, -68.2f, -174.0f, -67.5f, + -174.2f, -66.8f, -174.5f, -65.4f, -174.7f, -64.7f, -174.8f, -64.0f, -175.0f, -63.3f, + -175.3f, -61.9f, -175.5f, -61.2f, -175.8f, -59.8f, -176.0f, -59.1f, -176.1f, -58.4f, + -176.3f, -57.7f, -176.6f, -56.3f, -176.8f, -55.6f, -176.9f, -54.9f, -177.1f, -54.2f, + -177.3f, -53.6f, -177.4f, -52.9f, -177.6f, -52.2f, -177.9f, -50.8f, -178.1f, -50.1f, + -178.2f, -49.4f, -178.2f, -48.7f, -177.8f, -48.1f, -177.1f, -46.9f, -176.7f, -46.3f, + -176.4f, -45.6f, -176.0f, -45.0f, -175.3f, -43.8f, -174.9f, -43.2f, -174.2f, -42.0f, + -173.4f, -40.7f, -173.1f, -40.1f, -172.7f, -39.5f, -172.0f, -38.3f, -171.6f, -37.7f, + -170.5f, -35.9f, -170.1f, -35.3f, -169.7f, -34.6f, -169.3f, -34.0f, -168.6f, -32.8f, + -168.2f, -32.2f, -166.3f, -29.2f, -165.9f, -28.6f, -163.2f, -24.4f, -162.8f, -23.8f, + -141.8f, 6.8f, -141.4f, 7.4f, -139.4f, 10.3f, -139.0f, 10.9f, -138.5f, 11.5f, + -138.1f, 12.1f, -137.3f, 13.2f, -136.9f, 13.8f, -136.0f, 15.0f, -135.6f, 15.6f, + -135.2f, 16.1f, -134.8f, 16.7f, -133.9f, 17.9f, -133.5f, 18.4f, -133.1f, 19.0f, + -131.8f, 20.7f, -131.4f, 21.3f, -130.1f, 23.0f, -129.7f, 23.6f, -128.4f, 25.3f, + -128.0f, 25.9f, -126.7f, 27.6f, -126.3f, 28.2f, -125.4f, 29.3f, -125.0f, 29.9f, + -124.1f, 31.0f, -123.7f, 31.6f, -122.8f, 32.7f, -122.4f, 33.3f, -121.5f, 34.4f, + -121.1f, 35.0f, -120.6f, 35.6f, -120.2f, 36.1f, -119.7f, 36.7f, -119.3f, 37.2f, + -118.9f, 37.8f, -118.4f, 38.4f, -118.0f, 38.9f, -117.5f, 39.5f, -117.1f, 40.0f, + -116.6f, 40.6f, -116.2f, 41.1f, -115.7f, 41.7f, -115.2f, 42.2f, -114.8f, 42.8f, + -114.3f, 43.3f, -113.9f, 43.9f, -113.4f, 44.4f, -112.4f, 45.5f, -112.0f, 46.0f, + -111.5f, 46.5f, -110.5f, 47.6f, -110.0f, 48.1f, -109.6f, 48.6f, -109.1f, 49.2f, + -108.6f, 49.7f, -107.7f, 50.8f, -107.2f, 51.3f, -105.7f, 52.9f, -105.3f, 53.4f, + -104.8f, 53.9f, -104.3f, 54.5f, -103.8f, 55.0f, -100.7f, 58.0f, -100.2f, 58.4f, + -99.7f, 58.9f, -99.1f, 59.3f, -97.2f, 60.3f, -96.5f, 60.1f, -95.9f, 59.7f, + -95.3f, 59.4f, -94.6f, 59.1f, -93.9f, 58.9f, -92.6f, 58.5f, -91.9f, 58.4f, + -91.2f, 58.2f, -90.5f, 58.1f, -89.7f, 58.0f, -89.0f, 57.9f, -86.2f, 57.6f, + -85.5f, 57.5f, -84.1f, 57.4f, -83.4f, 57.3f, -82.6f, 57.3f, -81.9f, 57.2f, + -81.2f, 57.2f, -80.5f, 57.1f, -79.8f, 57.1f, -78.4f, 57.0f, -77.7f, 57.0f, + -75.5f, 56.9f, -74.8f, 56.9f, -71.9f, 56.8f, -71.2f, 56.8f, 0.0f, 56.8f, +}; + +constexpr std::array<float, 199 * 2> gc_body = { + 0.0f, -138.03f, -4.91f, -138.01f, -8.02f, -137.94f, -11.14f, -137.82f, -14.25f, + -137.67f, -17.37f, -137.48f, -20.48f, -137.25f, -23.59f, -137.0f, -26.69f, -136.72f, + -29.8f, -136.41f, -32.9f, -136.07f, -35.99f, -135.71f, -39.09f, -135.32f, -42.18f, + -134.91f, -45.27f, -134.48f, -48.35f, -134.03f, -51.43f, -133.55f, -54.51f, -133.05f, + -57.59f, -132.52f, -60.66f, -131.98f, -63.72f, -131.41f, -66.78f, -130.81f, -69.84f, + -130.2f, -72.89f, -129.56f, -75.94f, -128.89f, -78.98f, -128.21f, -82.02f, -127.49f, + -85.05f, -126.75f, -88.07f, -125.99f, -91.09f, -125.19f, -94.1f, -124.37f, -97.1f, + -123.52f, -100.09f, -122.64f, -103.07f, -121.72f, -106.04f, -120.77f, -109.0f, -119.79f, + -111.95f, -118.77f, -114.88f, -117.71f, -117.8f, -116.61f, -120.7f, -115.46f, -123.58f, + -114.27f, -126.44f, -113.03f, -129.27f, -111.73f, -132.08f, -110.38f, -134.86f, -108.96f, + -137.6f, -107.47f, -140.3f, -105.91f, -142.95f, -104.27f, -145.55f, -102.54f, -148.07f, + -100.71f, -150.51f, -98.77f, -152.86f, -96.71f, -155.09f, -94.54f, -157.23f, -92.27f, + -159.26f, -89.9f, -161.2f, -87.46f, -163.04f, -84.94f, -164.78f, -82.35f, -166.42f, + -79.7f, -167.97f, -77.0f, -169.43f, -74.24f, -170.8f, -71.44f, -172.09f, -68.6f, + -173.29f, -65.72f, -174.41f, -62.81f, -175.45f, -59.87f, -176.42f, -56.91f, -177.31f, + -53.92f, -178.14f, -50.91f, -178.9f, -47.89f, -179.6f, -44.85f, -180.24f, -41.8f, + -180.82f, -38.73f, -181.34f, -35.66f, -181.8f, -32.57f, -182.21f, -29.48f, -182.57f, + -26.38f, -182.88f, -23.28f, -183.15f, -20.17f, -183.36f, -17.06f, -183.54f, -13.95f, + -183.71f, -10.84f, -184.0f, -7.73f, -184.23f, -4.62f, -184.44f, -1.51f, -184.62f, + 1.6f, -184.79f, 4.72f, -184.95f, 7.83f, -185.11f, 10.95f, -185.25f, 14.06f, + -185.38f, 17.18f, -185.51f, 20.29f, -185.63f, 23.41f, -185.74f, 26.53f, -185.85f, + 29.64f, -185.95f, 32.76f, -186.04f, 35.88f, -186.12f, 39.0f, -186.19f, 42.11f, + -186.26f, 45.23f, -186.32f, 48.35f, -186.37f, 51.47f, -186.41f, 54.59f, -186.44f, + 57.7f, -186.46f, 60.82f, -186.46f, 63.94f, -186.44f, 70.18f, -186.41f, 73.3f, + -186.36f, 76.42f, -186.3f, 79.53f, -186.22f, 82.65f, -186.12f, 85.77f, -185.99f, + 88.88f, -185.84f, 92.0f, -185.66f, 95.11f, -185.44f, 98.22f, -185.17f, 101.33f, + -184.85f, 104.43f, -184.46f, 107.53f, -183.97f, 110.61f, -183.37f, 113.67f, -182.65f, + 116.7f, -181.77f, 119.69f, -180.71f, 122.62f, -179.43f, 125.47f, -177.89f, 128.18f, + -176.05f, 130.69f, -173.88f, 132.92f, -171.36f, 134.75f, -168.55f, 136.1f, -165.55f, + 136.93f, -162.45f, 137.29f, -156.23f, 137.03f, -153.18f, 136.41f, -150.46f, 134.9f, + -148.14f, 132.83f, -146.14f, 130.43f, -144.39f, 127.85f, -142.83f, 125.16f, -141.41f, + 122.38f, -140.11f, 119.54f, -138.9f, 116.67f, -137.77f, 113.76f, -136.7f, 110.84f, + -135.68f, 107.89f, -134.71f, 104.93f, -133.77f, 101.95f, -132.86f, 98.97f, -131.97f, + 95.98f, -131.09f, 92.99f, -130.23f, 89.99f, -129.36f, 86.99f, -128.49f, 84.0f, + -127.63f, 81.0f, -126.76f, 78.01f, -125.9f, 75.01f, -124.17f, 69.02f, -123.31f, + 66.02f, -121.59f, 60.03f, -120.72f, 57.03f, -119.86f, 54.03f, -118.13f, 48.04f, + -117.27f, 45.04f, -115.55f, 39.05f, -114.68f, 36.05f, -113.82f, 33.05f, -112.96f, + 30.06f, -110.4f, 28.29f, -107.81f, 26.55f, -105.23f, 24.8f, -97.48f, 19.55f, + -94.9f, 17.81f, -92.32f, 16.06f, -87.15f, 12.56f, -84.57f, 10.81f, -81.99f, + 9.07f, -79.4f, 7.32f, -76.82f, 5.57f, -69.07f, 0.33f, -66.49f, -1.42f, + -58.74f, -6.66f, -56.16f, -8.41f, -48.4f, -13.64f, -45.72f, -15.22f, -42.93f, + -16.62f, -40.07f, -17.86f, -37.15f, -18.96f, -34.19f, -19.94f, -31.19f, -20.79f, + -28.16f, -21.55f, -25.12f, -22.21f, -22.05f, -22.79f, -18.97f, -23.28f, -15.88f, + -23.7f, -12.78f, -24.05f, -9.68f, -24.33f, -6.57f, -24.55f, -3.45f, -24.69f, + 0.0f, -24.69f, +}; + +constexpr std::array<float, 99 * 2> gc_left_body = { + -74.59f, -97.22f, -70.17f, -94.19f, -65.95f, -90.89f, -62.06f, -87.21f, -58.58f, + -83.14f, -55.58f, -78.7f, -53.08f, -73.97f, -51.05f, -69.01f, -49.46f, -63.89f, + -48.24f, -58.67f, -47.36f, -53.39f, -46.59f, -48.09f, -45.7f, -42.8f, -44.69f, + -37.54f, -43.54f, -32.31f, -42.25f, -27.11f, -40.8f, -21.95f, -39.19f, -16.84f, + -37.38f, -11.8f, -35.34f, -6.84f, -33.04f, -2.0f, -30.39f, 2.65f, -27.26f, + 7.0f, -23.84f, 11.11f, -21.19f, 15.76f, -19.18f, 20.73f, -17.73f, 25.88f, + -16.82f, 31.16f, -16.46f, 36.5f, -16.7f, 41.85f, -17.63f, 47.13f, -19.31f, + 52.21f, -21.8f, 56.95f, -24.91f, 61.3f, -28.41f, 65.36f, -32.28f, 69.06f, + -36.51f, 72.35f, -41.09f, 75.13f, -45.97f, 77.32f, -51.1f, 78.86f, -56.39f, + 79.7f, -61.74f, 79.84f, -67.07f, 79.3f, -72.3f, 78.15f, -77.39f, 76.48f, + -82.29f, 74.31f, -86.76f, 71.37f, -90.7f, 67.75f, -94.16f, 63.66f, -97.27f, + 59.3f, -100.21f, 54.81f, -103.09f, 50.3f, -106.03f, 45.82f, -109.11f, 41.44f, + -112.37f, 37.19f, -115.85f, 33.11f, -119.54f, 29.22f, -123.45f, 25.56f, -127.55f, + 22.11f, -131.77f, 18.81f, -136.04f, 15.57f, -140.34f, 12.37f, -144.62f, 9.15f, + -148.86f, 5.88f, -153.03f, 2.51f, -157.05f, -1.03f, -160.83f, -4.83f, -164.12f, + -9.05f, -166.71f, -13.73f, -168.91f, -18.62f, -170.77f, -23.64f, -172.3f, -28.78f, + -173.49f, -34.0f, -174.3f, -39.3f, -174.72f, -44.64f, -174.72f, -49.99f, -174.28f, + -55.33f, -173.37f, -60.61f, -172.0f, -65.79f, -170.17f, -70.82f, -167.79f, -75.62f, + -164.84f, -80.09f, -161.43f, -84.22f, -157.67f, -88.03f, -153.63f, -91.55f, -149.37f, + -94.81f, -144.94f, -97.82f, -140.37f, -100.61f, -135.65f, -103.16f, -130.73f, -105.26f, + -125.62f, -106.86f, -120.37f, -107.95f, -115.05f, -108.56f, -109.7f, -108.69f, -104.35f, + -108.36f, -99.05f, -107.6f, -93.82f, -106.41f, -88.72f, -104.79f, -83.78f, -102.7f, +}; + +constexpr std::array<float, 47 * 2> left_gc_trigger = { + -99.69f, -125.04f, -101.81f, -126.51f, -104.02f, -127.85f, -106.3f, -129.06f, -108.65f, + -130.12f, -111.08f, -130.99f, -113.58f, -131.62f, -116.14f, -131.97f, -121.26f, -131.55f, + -123.74f, -130.84f, -126.17f, -129.95f, -128.53f, -128.9f, -130.82f, -127.71f, -133.03f, + -126.38f, -135.15f, -124.92f, -137.18f, -123.32f, -139.11f, -121.6f, -140.91f, -119.75f, + -142.55f, -117.77f, -144.0f, -115.63f, -145.18f, -113.34f, -146.17f, -110.95f, -147.05f, + -108.53f, -147.87f, -106.08f, -148.64f, -103.61f, -149.37f, -101.14f, -149.16f, -100.12f, + -147.12f, -101.71f, -144.99f, -103.16f, -142.8f, -104.53f, -140.57f, -105.83f, -138.31f, + -107.08f, -136.02f, -108.27f, -133.71f, -109.42f, -131.38f, -110.53f, -129.04f, -111.61f, + -126.68f, -112.66f, -124.31f, -113.68f, -121.92f, -114.67f, -119.53f, -115.64f, -117.13f, + -116.58f, -114.72f, -117.51f, -112.3f, -118.41f, -109.87f, -119.29f, -107.44f, -120.16f, + -105.0f, -121.0f, -100.11f, -122.65f, +}; + +constexpr std::array<float, 50 * 2> gc_button_x = { + 142.1f, -50.67f, 142.44f, -48.65f, 142.69f, -46.62f, 142.8f, -44.57f, 143.0f, -42.54f, + 143.56f, -40.57f, 144.42f, -38.71f, 145.59f, -37.04f, 147.08f, -35.64f, 148.86f, -34.65f, + 150.84f, -34.11f, 152.88f, -34.03f, 154.89f, -34.38f, 156.79f, -35.14f, 158.49f, -36.28f, + 159.92f, -37.74f, 161.04f, -39.45f, 161.85f, -41.33f, 162.4f, -43.3f, 162.72f, -45.32f, + 162.85f, -47.37f, 162.82f, -49.41f, 162.67f, -51.46f, 162.39f, -53.48f, 162.0f, -55.5f, + 161.51f, -57.48f, 160.9f, -59.44f, 160.17f, -61.35f, 159.25f, -63.18f, 158.19f, -64.93f, + 157.01f, -66.61f, 155.72f, -68.2f, 154.31f, -69.68f, 152.78f, -71.04f, 151.09f, -72.2f, + 149.23f, -73.04f, 147.22f, -73.36f, 145.19f, -73.11f, 143.26f, -72.42f, 141.51f, -71.37f, + 140.0f, -69.99f, 138.82f, -68.32f, 138.13f, -66.4f, 138.09f, -64.36f, 138.39f, -62.34f, + 139.05f, -60.41f, 139.91f, -58.55f, 140.62f, -56.63f, 141.21f, -54.67f, 141.67f, -52.67f, +}; + +constexpr std::array<float, 50 * 2> gc_button_y = { + 104.02f, -75.23f, 106.01f, -75.74f, 108.01f, -76.15f, 110.04f, -76.42f, 112.05f, -76.78f, + 113.97f, -77.49f, 115.76f, -78.49f, 117.33f, -79.79f, 118.6f, -81.39f, 119.46f, -83.25f, + 119.84f, -85.26f, 119.76f, -87.3f, 119.24f, -89.28f, 118.33f, -91.11f, 117.06f, -92.71f, + 115.49f, -94.02f, 113.7f, -95.01f, 111.77f, -95.67f, 109.76f, -96.05f, 107.71f, -96.21f, + 105.67f, -96.18f, 103.63f, -95.99f, 101.61f, -95.67f, 99.61f, -95.24f, 97.63f, -94.69f, + 95.69f, -94.04f, 93.79f, -93.28f, 91.94f, -92.4f, 90.19f, -91.34f, 88.53f, -90.14f, + 86.95f, -88.84f, 85.47f, -87.42f, 84.1f, -85.9f, 82.87f, -84.26f, 81.85f, -82.49f, + 81.15f, -80.57f, 81.0f, -78.54f, 81.41f, -76.54f, 82.24f, -74.67f, 83.43f, -73.01f, + 84.92f, -71.61f, 86.68f, -70.57f, 88.65f, -70.03f, 90.69f, -70.15f, 92.68f, -70.61f, + 94.56f, -71.42f, 96.34f, -72.43f, 98.2f, -73.29f, 100.11f, -74.03f, 102.06f, -74.65f, +}; + +constexpr std::array<float, 47 * 2> gc_button_z = { + 95.74f, -126.41f, 98.34f, -126.38f, 100.94f, -126.24f, 103.53f, -126.01f, 106.11f, -125.7f, + 108.69f, -125.32f, 111.25f, -124.87f, 113.8f, -124.34f, 116.33f, -123.73f, 118.84f, -123.05f, + 121.33f, -122.3f, 123.79f, -121.47f, 126.23f, -120.56f, 128.64f, -119.58f, 131.02f, -118.51f, + 133.35f, -117.37f, 135.65f, -116.14f, 137.9f, -114.84f, 140.1f, -113.46f, 142.25f, -111.99f, + 144.35f, -110.45f, 146.38f, -108.82f, 148.35f, -107.13f, 150.25f, -105.35f, 151.89f, -103.38f, + 151.43f, -100.86f, 149.15f, -100.15f, 146.73f, -101.06f, 144.36f, -102.12f, 141.98f, -103.18f, + 139.6f, -104.23f, 137.22f, -105.29f, 134.85f, -106.35f, 132.47f, -107.41f, 127.72f, -109.53f, + 125.34f, -110.58f, 122.96f, -111.64f, 120.59f, -112.7f, 118.21f, -113.76f, 113.46f, -115.88f, + 111.08f, -116.93f, 108.7f, -117.99f, 106.33f, -119.05f, 103.95f, -120.11f, 99.2f, -122.23f, + 96.82f, -123.29f, 94.44f, -124.34f, +}; + +constexpr std::array<float, 84 * 2> left_joycon_body = { + -145.0f, -78.9f, -145.0f, -77.9f, -145.0f, 85.6f, -145.0f, 85.6f, -168.3f, 85.5f, + -169.3f, 85.4f, -171.3f, 85.1f, -172.3f, 84.9f, -173.4f, 84.7f, -174.3f, 84.5f, + -175.3f, 84.2f, -176.3f, 83.8f, -177.3f, 83.5f, -178.2f, 83.1f, -179.2f, 82.7f, + -180.1f, 82.2f, -181.0f, 81.8f, -181.9f, 81.3f, -182.8f, 80.7f, -183.7f, 80.2f, + -184.5f, 79.6f, -186.2f, 78.3f, -186.9f, 77.7f, -187.7f, 77.0f, -189.2f, 75.6f, + -189.9f, 74.8f, -190.6f, 74.1f, -191.3f, 73.3f, -191.9f, 72.5f, -192.5f, 71.6f, + -193.1f, 70.8f, -193.7f, 69.9f, -194.3f, 69.1f, -194.8f, 68.2f, -196.2f, 65.5f, + -196.6f, 64.5f, -197.0f, 63.6f, -197.4f, 62.6f, -198.1f, 60.7f, -198.4f, 59.7f, + -198.6f, 58.7f, -199.2f, 55.6f, -199.3f, 54.6f, -199.5f, 51.5f, -199.5f, 50.5f, + -199.5f, -49.4f, -199.4f, -50.5f, -199.3f, -51.5f, -199.1f, -52.5f, -198.2f, -56.5f, + -197.9f, -57.5f, -197.2f, -59.4f, -196.8f, -60.4f, -196.4f, -61.3f, -195.9f, -62.2f, + -194.3f, -64.9f, -193.7f, -65.7f, -193.1f, -66.6f, -192.5f, -67.4f, -191.8f, -68.2f, + -191.2f, -68.9f, -190.4f, -69.7f, -188.2f, -71.8f, -187.4f, -72.5f, -186.6f, -73.1f, + -185.8f, -73.8f, -185.0f, -74.4f, -184.1f, -74.9f, -183.2f, -75.5f, -182.4f, -76.0f, + -181.5f, -76.5f, -179.6f, -77.5f, -178.7f, -77.9f, -177.8f, -78.4f, -176.8f, -78.8f, + -175.9f, -79.1f, -174.9f, -79.5f, -173.9f, -79.8f, -170.9f, -80.6f, -169.9f, -80.8f, + -167.9f, -81.1f, -166.9f, -81.2f, -165.8f, -81.2f, -145.0f, -80.9f, +}; + +constexpr std::array<float, 84 * 2> left_joycon_trigger = { + -166.8f, -83.3f, -167.9f, -83.2f, -168.9f, -83.1f, -170.0f, -83.0f, -171.0f, -82.8f, + -172.1f, -82.6f, -173.1f, -82.4f, -174.2f, -82.1f, -175.2f, -81.9f, -176.2f, -81.5f, + -177.2f, -81.2f, -178.2f, -80.8f, -180.1f, -80.0f, -181.1f, -79.5f, -182.0f, -79.0f, + -183.0f, -78.5f, -183.9f, -78.0f, -184.8f, -77.4f, -185.7f, -76.9f, -186.6f, -76.3f, + -187.4f, -75.6f, -188.3f, -75.0f, -189.1f, -74.3f, -192.2f, -71.5f, -192.9f, -70.7f, + -193.7f, -69.9f, -194.3f, -69.1f, -195.0f, -68.3f, -195.6f, -67.4f, -196.8f, -65.7f, + -197.3f, -64.7f, -197.8f, -63.8f, -198.2f, -62.8f, -198.9f, -60.8f, -198.6f, -59.8f, + -197.6f, -59.7f, -196.6f, -60.0f, -195.6f, -60.5f, -194.7f, -60.9f, -193.7f, -61.4f, + -192.8f, -61.9f, -191.8f, -62.4f, -190.9f, -62.8f, -189.9f, -63.3f, -189.0f, -63.8f, + -187.1f, -64.8f, -186.2f, -65.2f, -185.2f, -65.7f, -184.3f, -66.2f, -183.3f, -66.7f, + -182.4f, -67.1f, -181.4f, -67.6f, -180.5f, -68.1f, -179.5f, -68.6f, -178.6f, -69.0f, + -177.6f, -69.5f, -176.7f, -70.0f, -175.7f, -70.5f, -174.8f, -70.9f, -173.8f, -71.4f, + -172.9f, -71.9f, -171.9f, -72.4f, -171.0f, -72.8f, -170.0f, -73.3f, -169.1f, -73.8f, + -168.1f, -74.3f, -167.2f, -74.7f, -166.2f, -75.2f, -165.3f, -75.7f, -164.3f, -76.2f, + -163.4f, -76.6f, -162.4f, -77.1f, -161.5f, -77.6f, -160.5f, -78.1f, -159.6f, -78.5f, + -158.7f, -79.0f, -157.7f, -79.5f, -156.8f, -80.0f, -155.8f, -80.4f, -154.9f, -80.9f, + -154.2f, -81.6f, -154.3f, -82.6f, -155.2f, -83.3f, -156.2f, -83.3f, +}; + +constexpr std::array<float, 70 * 2> handheld_body = { + -137.3f, -81.9f, -137.6f, -81.8f, -137.8f, -81.6f, -138.0f, -81.3f, -138.1f, -81.1f, + -138.1f, -80.8f, -138.2f, -78.7f, -138.2f, -78.4f, -138.3f, -78.1f, -138.7f, -77.3f, + -138.9f, -77.0f, -139.0f, -76.8f, -139.2f, -76.5f, -139.5f, -76.3f, -139.7f, -76.1f, + -139.9f, -76.0f, -140.2f, -75.8f, -140.5f, -75.7f, -140.7f, -75.6f, -141.0f, -75.5f, + -141.9f, -75.3f, -142.2f, -75.3f, -142.5f, -75.2f, -143.0f, -74.9f, -143.2f, -74.7f, + -143.3f, -74.4f, -143.0f, -74.1f, -143.0f, 85.3f, -143.0f, 85.6f, -142.7f, 85.8f, + -142.4f, 85.9f, -142.2f, 85.9f, 143.0f, 85.6f, 143.1f, 85.4f, 143.3f, 85.1f, + 143.0f, 84.8f, 143.0f, -74.9f, 142.8f, -75.1f, 142.5f, -75.2f, 141.9f, -75.3f, + 141.6f, -75.3f, 141.3f, -75.4f, 141.1f, -75.4f, 140.8f, -75.5f, 140.5f, -75.7f, + 140.2f, -75.8f, 140.0f, -76.0f, 139.7f, -76.1f, 139.5f, -76.3f, 139.1f, -76.8f, + 138.9f, -77.0f, 138.6f, -77.5f, 138.4f, -77.8f, 138.3f, -78.1f, 138.3f, -78.3f, + 138.2f, -78.6f, 138.2f, -78.9f, 138.1f, -79.2f, 138.1f, -79.5f, 138.0f, -81.3f, + 137.8f, -81.6f, 137.6f, -81.8f, 137.3f, -81.9f, 137.1f, -81.9f, 120.0f, -70.0f, + -120.0f, -70.0f, -120.0f, 70.0f, 120.0f, 70.0f, 120.0f, -70.0f, 137.1f, -81.9f, +}; + +constexpr std::array<float, 40 * 2> handheld_bezel = { + -131.4f, -75.9f, -132.2f, -75.7f, -132.9f, -75.3f, -134.2f, -74.3f, -134.7f, -73.6f, + -135.1f, -72.8f, -135.4f, -72.0f, -135.5f, -71.2f, -135.5f, -70.4f, -135.2f, 76.7f, + -134.8f, 77.5f, -134.3f, 78.1f, -133.7f, 78.8f, -133.1f, 79.2f, -132.3f, 79.6f, + -131.5f, 79.9f, -130.7f, 80.0f, -129.8f, 80.0f, 132.2f, 79.7f, 133.0f, 79.3f, + 133.7f, 78.8f, 134.3f, 78.3f, 134.8f, 77.6f, 135.1f, 76.8f, 135.5f, 75.2f, + 135.5f, 74.3f, 135.2f, -72.7f, 134.8f, -73.5f, 134.4f, -74.2f, 133.8f, -74.8f, + 133.1f, -75.3f, 132.3f, -75.6f, 130.7f, -76.0f, 129.8f, -76.0f, -112.9f, -62.2f, + 112.9f, -62.2f, 112.9f, 62.2f, -112.9f, 62.2f, -112.9f, -62.2f, 129.8f, -76.0f, +}; + +constexpr std::array<float, 58 * 2> handheld_buttons = { + -82.48f, -82.95f, -82.53f, -82.95f, -106.69f, -82.96f, -106.73f, -82.98f, -106.78f, -83.01f, + -106.81f, -83.05f, -106.83f, -83.1f, -106.83f, -83.15f, -106.82f, -83.93f, -106.81f, -83.99f, + -106.8f, -84.04f, -106.78f, -84.08f, -106.76f, -84.13f, -106.73f, -84.18f, -106.7f, -84.22f, + -106.6f, -84.34f, -106.56f, -84.37f, -106.51f, -84.4f, -106.47f, -84.42f, -106.42f, -84.45f, + -106.37f, -84.47f, -106.32f, -84.48f, -106.17f, -84.5f, -98.9f, -84.48f, -98.86f, -84.45f, + -98.83f, -84.41f, -98.81f, -84.36f, -98.8f, -84.31f, -98.8f, -84.26f, -98.79f, -84.05f, + -90.26f, -84.1f, -90.26f, -84.15f, -90.25f, -84.36f, -90.23f, -84.41f, -90.2f, -84.45f, + -90.16f, -84.48f, -90.11f, -84.5f, -82.79f, -84.49f, -82.74f, -84.48f, -82.69f, -84.46f, + -82.64f, -84.45f, -82.59f, -84.42f, -82.55f, -84.4f, -82.5f, -84.37f, -82.46f, -84.33f, + -82.42f, -84.3f, -82.39f, -84.26f, -82.3f, -84.13f, -82.28f, -84.08f, -82.25f, -83.98f, + -82.24f, -83.93f, -82.23f, -83.83f, -82.23f, -83.78f, -82.24f, -83.1f, -82.26f, -83.05f, + -82.29f, -83.01f, -82.33f, -82.97f, -82.38f, -82.95f, +}; + +constexpr std::array<float, 47 * 2> left_joycon_slider = { + -23.7f, -118.2f, -23.7f, -117.3f, -23.7f, 96.6f, -22.8f, 96.6f, -21.5f, 97.2f, -21.5f, + 98.1f, -21.2f, 106.7f, -20.8f, 107.5f, -20.1f, 108.2f, -19.2f, 108.2f, -16.4f, 108.1f, + -15.8f, 107.5f, -15.8f, 106.5f, -15.8f, 62.8f, -16.3f, 61.9f, -15.8f, 61.0f, -17.3f, + 60.3f, -19.1f, 58.9f, -19.1f, 58.1f, -19.1f, 57.2f, -19.1f, 34.5f, -17.9f, 33.9f, + -17.2f, 33.2f, -16.6f, 32.4f, -16.2f, 31.6f, -15.8f, 30.7f, -15.8f, 29.7f, -15.8f, + 28.8f, -15.8f, -46.4f, -16.3f, -47.3f, -15.8f, -48.1f, -17.4f, -48.8f, -19.1f, -49.4f, + -19.1f, -50.1f, -19.1f, -51.0f, -19.1f, -51.9f, -19.1f, -73.7f, -19.1f, -74.5f, -17.5f, + -75.2f, -16.4f, -76.7f, -16.0f, -77.6f, -15.8f, -78.5f, -15.8f, -79.4f, -15.8f, -80.4f, + -15.8f, -118.2f, -15.8f, -118.2f, -18.3f, -118.2f, +}; + +constexpr std::array<float, 66 * 2> left_joycon_sideview = { + -158.8f, -133.5f, -159.8f, -133.5f, -173.5f, -133.3f, -174.5f, -133.0f, -175.4f, -132.6f, + -176.2f, -132.1f, -177.0f, -131.5f, -177.7f, -130.9f, -178.3f, -130.1f, -179.4f, -128.5f, + -179.8f, -127.6f, -180.4f, -125.7f, -180.6f, -124.7f, -180.7f, -123.8f, -180.7f, -122.8f, + -180.0f, 128.8f, -179.6f, 129.7f, -179.1f, 130.5f, -177.9f, 132.1f, -177.2f, 132.7f, + -176.4f, 133.3f, -175.6f, 133.8f, -174.7f, 134.3f, -173.8f, 134.6f, -172.8f, 134.8f, + -170.9f, 135.0f, -169.9f, 135.0f, -156.1f, 134.8f, -155.2f, 134.6f, -154.2f, 134.3f, + -153.3f, 134.0f, -152.4f, 133.6f, -151.6f, 133.1f, -150.7f, 132.6f, -149.9f, 132.0f, + -149.2f, 131.4f, -148.5f, 130.7f, -147.1f, 129.2f, -146.5f, 128.5f, -146.0f, 127.7f, + -145.5f, 126.8f, -145.0f, 126.0f, -144.6f, 125.1f, -144.2f, 124.1f, -143.9f, 123.2f, + -143.7f, 122.2f, -143.6f, 121.3f, -143.5f, 120.3f, -143.5f, 119.3f, -144.4f, -123.4f, + -144.8f, -124.3f, -145.3f, -125.1f, -145.8f, -126.0f, -146.3f, -126.8f, -147.0f, -127.5f, + -147.6f, -128.3f, -148.3f, -129.0f, -149.0f, -129.6f, -149.8f, -130.3f, -150.6f, -130.8f, + -151.4f, -131.4f, -152.2f, -131.9f, -153.1f, -132.3f, -155.9f, -133.3f, -156.8f, -133.5f, + -157.8f, -133.5f, +}; + +constexpr std::array<float, 40 * 2> left_joycon_body_trigger = { + -146.1f, -124.3f, -146.0f, -122.0f, -145.8f, -119.7f, -145.7f, -117.4f, -145.4f, -112.8f, + -145.3f, -110.5f, -145.0f, -105.9f, -144.9f, -103.6f, -144.6f, -99.1f, -144.5f, -96.8f, + -144.5f, -89.9f, -144.5f, -87.6f, -144.5f, -83.0f, -144.5f, -80.7f, -144.5f, -80.3f, + -142.4f, -82.4f, -141.4f, -84.5f, -140.2f, -86.4f, -138.8f, -88.3f, -137.4f, -90.1f, + -134.5f, -93.6f, -133.0f, -95.3f, -130.0f, -98.8f, -128.5f, -100.6f, -127.1f, -102.4f, + -125.8f, -104.3f, -124.7f, -106.3f, -123.9f, -108.4f, -125.1f, -110.2f, -127.4f, -110.3f, + -129.7f, -110.3f, -134.2f, -110.5f, -136.4f, -111.4f, -138.1f, -112.8f, -139.4f, -114.7f, + -140.5f, -116.8f, -141.4f, -118.9f, -143.3f, -123.1f, -144.6f, -124.9f, -146.2f, -126.0f, +}; + +constexpr std::array<float, 49 * 2> left_joycon_topview = { + -184.8f, -20.8f, -185.6f, -21.1f, -186.4f, -21.5f, -187.1f, -22.1f, -187.8f, -22.6f, + -188.4f, -23.2f, -189.6f, -24.5f, -190.2f, -25.2f, -190.7f, -25.9f, -191.1f, -26.7f, + -191.4f, -27.5f, -191.6f, -28.4f, -191.7f, -29.2f, -191.7f, -30.1f, -191.5f, -47.7f, + -191.2f, -48.5f, -191.0f, -49.4f, -190.7f, -50.2f, -190.3f, -51.0f, -190.0f, -51.8f, + -189.6f, -52.6f, -189.1f, -53.4f, -188.6f, -54.1f, -187.5f, -55.4f, -186.9f, -56.1f, + -186.2f, -56.7f, -185.5f, -57.2f, -184.0f, -58.1f, -183.3f, -58.5f, -182.5f, -58.9f, + -181.6f, -59.2f, -180.8f, -59.5f, -179.9f, -59.7f, -179.1f, -59.9f, -178.2f, -60.0f, + -174.7f, -60.1f, -168.5f, -60.2f, -162.4f, -60.3f, -156.2f, -60.4f, -149.2f, -60.5f, + -143.0f, -60.6f, -136.9f, -60.7f, -130.7f, -60.8f, -123.7f, -60.9f, -117.5f, -61.0f, + -110.5f, -61.1f, -94.4f, -60.4f, -94.4f, -59.5f, -94.4f, -20.6f, +}; + +constexpr std::array<float, 41 * 2> left_joycon_slider_topview = { + -95.1f, -51.5f, -95.0f, -51.5f, -91.2f, -51.6f, -91.2f, -51.7f, -91.1f, -52.4f, -91.1f, -52.6f, + -91.0f, -54.1f, -86.3f, -54.0f, -86.0f, -53.9f, -85.9f, -53.8f, -85.6f, -53.4f, -85.5f, -53.2f, + -85.5f, -53.1f, -85.4f, -52.9f, -85.4f, -52.8f, -85.3f, -52.4f, -85.3f, -52.3f, -85.4f, -27.2f, + -85.4f, -27.1f, -85.5f, -27.0f, -85.5f, -26.9f, -85.6f, -26.7f, -85.6f, -26.6f, -85.7f, -26.5f, + -85.9f, -26.4f, -86.0f, -26.3f, -86.4f, -26.0f, -86.5f, -25.9f, -86.7f, -25.8f, -87.1f, -25.7f, + -90.4f, -25.8f, -90.7f, -25.9f, -90.8f, -26.0f, -90.9f, -26.3f, -91.0f, -26.4f, -91.0f, -26.5f, + -91.1f, -26.7f, -91.1f, -26.9f, -91.2f, -28.9f, -95.2f, -29.1f, -95.2f, -29.2f, +}; + +constexpr std::array<float, 42 * 2> left_joycon_sideview_zl = { + -148.9f, -128.2f, -148.7f, -126.6f, -148.4f, -124.9f, -148.2f, -123.3f, -147.9f, -121.7f, + -147.7f, -120.1f, -147.4f, -118.5f, -147.2f, -116.9f, -146.9f, -115.3f, -146.4f, -112.1f, + -146.1f, -110.5f, -145.9f, -108.9f, -145.6f, -107.3f, -144.2f, -107.3f, -142.6f, -107.5f, + -141.0f, -107.8f, -137.8f, -108.3f, -136.2f, -108.6f, -131.4f, -109.4f, -129.8f, -109.7f, + -125.6f, -111.4f, -124.5f, -112.7f, -123.9f, -114.1f, -123.8f, -115.8f, -123.8f, -117.4f, + -123.9f, -120.6f, -124.5f, -122.1f, -125.8f, -123.1f, -127.4f, -123.4f, -129.0f, -123.6f, + -130.6f, -124.0f, -132.1f, -124.4f, -133.7f, -124.8f, -135.3f, -125.3f, -136.8f, -125.9f, + -138.3f, -126.4f, -139.9f, -126.9f, -141.4f, -127.5f, -142.9f, -128.0f, -144.5f, -128.5f, + -146.0f, -129.0f, -147.6f, -129.4f, +}; + +constexpr std::array<float, 72 * 2> left_joystick_sideview = { + -14.7f, -3.8f, -15.2f, -5.6f, -15.2f, -7.6f, -15.5f, -17.6f, -17.4f, -18.3f, -19.4f, -18.2f, + -21.3f, -17.6f, -22.8f, -16.4f, -23.4f, -14.5f, -23.4f, -12.5f, -24.1f, -8.6f, -24.8f, -6.7f, + -25.3f, -4.8f, -25.7f, -2.8f, -25.9f, -0.8f, -26.0f, 1.2f, -26.0f, 3.2f, -25.8f, 5.2f, + -25.5f, 7.2f, -25.0f, 9.2f, -24.4f, 11.1f, -23.7f, 13.0f, -23.4f, 14.9f, -23.4f, 16.9f, + -23.3f, 18.9f, -22.0f, 20.5f, -20.2f, 21.3f, -18.3f, 21.6f, -16.3f, 21.4f, -15.3f, 19.9f, + -15.3f, 17.8f, -15.2f, 7.8f, -13.5f, 6.4f, -12.4f, 7.2f, -11.4f, 8.9f, -10.2f, 10.5f, + -8.7f, 11.8f, -7.1f, 13.0f, -5.3f, 14.0f, -3.5f, 14.7f, -1.5f, 15.0f, 0.5f, 15.0f, + 2.5f, 14.7f, 4.4f, 14.2f, 6.3f, 13.4f, 8.0f, 12.4f, 9.6f, 11.1f, 10.9f, 9.6f, + 12.0f, 7.9f, 12.7f, 6.0f, 13.2f, 4.1f, 13.3f, 2.1f, 13.2f, 0.1f, 12.9f, -1.9f, + 12.2f, -3.8f, 11.3f, -5.6f, 10.2f, -7.2f, 8.8f, -8.6f, 7.1f, -9.8f, 5.4f, -10.8f, + 3.5f, -11.5f, 1.5f, -11.9f, -0.5f, -12.0f, -2.5f, -11.8f, -4.4f, -11.3f, -6.2f, -10.4f, + -8.0f, -9.4f, -9.6f, -8.2f, -10.9f, -6.7f, -11.9f, -4.9f, -12.8f, -3.2f, -13.5f, -3.8f, +}; + +constexpr std::array<float, 63 * 2> left_joystick_L_topview = { + -186.7f, -43.7f, -186.4f, -43.7f, -110.6f, -43.4f, -110.6f, -43.1f, -110.7f, -34.3f, + -110.7f, -34.0f, -110.8f, -33.7f, -111.1f, -32.9f, -111.2f, -32.6f, -111.4f, -32.3f, + -111.5f, -32.1f, -111.7f, -31.8f, -111.8f, -31.5f, -112.0f, -31.3f, -112.2f, -31.0f, + -112.4f, -30.8f, -112.8f, -30.3f, -113.0f, -30.1f, -114.1f, -29.1f, -114.3f, -28.9f, + -114.6f, -28.7f, -114.8f, -28.6f, -115.1f, -28.4f, -115.3f, -28.3f, -115.6f, -28.1f, + -115.9f, -28.0f, -116.4f, -27.8f, -116.7f, -27.7f, -117.3f, -27.6f, -117.6f, -27.5f, + -182.9f, -27.6f, -183.5f, -27.7f, -183.8f, -27.8f, -184.4f, -27.9f, -184.6f, -28.1f, + -184.9f, -28.2f, -185.4f, -28.5f, -185.7f, -28.7f, -185.9f, -28.8f, -186.2f, -29.0f, + -186.4f, -29.2f, -187.0f, -29.9f, -187.2f, -30.1f, -187.6f, -30.6f, -187.8f, -30.8f, + -187.9f, -31.1f, -188.1f, -31.3f, -188.2f, -31.6f, -188.4f, -31.9f, -188.5f, -32.1f, + -188.6f, -32.4f, -188.8f, -33.3f, -188.9f, -33.6f, -188.9f, -33.9f, -188.8f, -39.9f, + -188.8f, -40.2f, -188.7f, -41.1f, -188.7f, -41.4f, -188.6f, -41.7f, -188.0f, -43.1f, + -187.9f, -43.4f, -187.6f, -43.6f, -187.3f, -43.7f, +}; + +constexpr std::array<float, 44 * 2> left_joystick_ZL_topview = { + -179.4f, -53.3f, -177.4f, -53.3f, -111.2f, -53.3f, -111.3f, -53.3f, -111.5f, -58.6f, + -111.8f, -60.5f, -112.2f, -62.4f, -113.1f, -66.1f, -113.8f, -68.0f, -114.5f, -69.8f, + -115.3f, -71.5f, -116.3f, -73.2f, -117.3f, -74.8f, -118.5f, -76.4f, -119.8f, -77.8f, + -121.2f, -79.1f, -122.8f, -80.2f, -124.4f, -81.2f, -126.2f, -82.0f, -128.1f, -82.6f, + -130.0f, -82.9f, -131.9f, -83.0f, -141.5f, -82.9f, -149.3f, -82.8f, -153.1f, -82.6f, + -155.0f, -82.1f, -156.8f, -81.6f, -158.7f, -80.9f, -160.4f, -80.2f, -162.2f, -79.3f, + -163.8f, -78.3f, -165.4f, -77.2f, -166.9f, -76.0f, -168.4f, -74.7f, -169.7f, -73.3f, + -172.1f, -70.3f, -173.2f, -68.7f, -174.2f, -67.1f, -175.2f, -65.4f, -176.1f, -63.7f, + -178.7f, -58.5f, -179.6f, -56.8f, -180.4f, -55.1f, -181.3f, -53.3f, +}; + +void PlayerControlPreview::DrawProBody(QPainter& p, const QPointF center) { + std::array<QPointF, pro_left_handle.size() / 2> qleft_handle; + std::array<QPointF, pro_left_handle.size() / 2> qright_handle; + std::array<QPointF, pro_body.size()> qbody; + constexpr int radius1 = 32; + + for (std::size_t point = 0; point < pro_left_handle.size() / 2; ++point) { + qleft_handle[point] = + center + QPointF(pro_left_handle[point * 2], pro_left_handle[point * 2 + 1]); + qright_handle[point] = + center + QPointF(-pro_left_handle[point * 2], pro_left_handle[point * 2 + 1]); + } + for (std::size_t point = 0; point < pro_body.size() / 2; ++point) { + qbody[point] = center + QPointF(pro_body[point * 2], pro_body[point * 2 + 1]); + qbody[pro_body.size() - 1 - point] = + center + QPointF(-pro_body[point * 2], pro_body[point * 2 + 1]); + } + + // Draw left handle body + p.setPen(colors.outline); + p.setBrush(colors.left); + DrawPolygon(p, qleft_handle); + + // Draw right handle body + p.setBrush(colors.right); + DrawPolygon(p, qright_handle); + + // Draw body + p.setBrush(colors.primary); + DrawPolygon(p, qbody); + + // Draw joycon circles + p.setBrush(colors.transparent); + p.drawEllipse(center + QPoint(-111, -55), radius1, radius1); + p.drawEllipse(center + QPoint(51, 0), radius1, radius1); +} + +void PlayerControlPreview::DrawGCBody(QPainter& p, const QPointF center) { + std::array<QPointF, gc_left_body.size() / 2> qleft_handle; + std::array<QPointF, gc_left_body.size() / 2> qright_handle; + std::array<QPointF, gc_body.size()> qbody; + std::array<QPointF, 8> left_hex; + std::array<QPointF, 8> right_hex; + constexpr float angle = 2 * 3.1415f / 8; + + for (std::size_t point = 0; point < gc_left_body.size() / 2; ++point) { + qleft_handle[point] = + center + QPointF(gc_left_body[point * 2], gc_left_body[point * 2 + 1]); + qright_handle[point] = + center + QPointF(-gc_left_body[point * 2], gc_left_body[point * 2 + 1]); + } + for (std::size_t point = 0; point < gc_body.size() / 2; ++point) { + qbody[point] = center + QPointF(gc_body[point * 2], gc_body[point * 2 + 1]); + qbody[gc_body.size() - 1 - point] = + center + QPointF(-gc_body[point * 2], gc_body[point * 2 + 1]); + } + for (std::size_t point = 0; point < 8; ++point) { + left_hex[point] = + center + QPointF(34 * std::cos(point * angle) - 111, 34 * std::sin(point * angle) - 44); + right_hex[point] = + center + QPointF(26 * std::cos(point * angle) + 61, 26 * std::sin(point * angle) + 37); + } + + // Draw body + p.setPen(colors.outline); + p.setBrush(colors.primary); + DrawPolygon(p, qbody); + + // Draw left handle body + p.setBrush(colors.left); + DrawPolygon(p, qleft_handle); + + // Draw right handle body + p.setBrush(colors.right); + DrawPolygon(p, qright_handle); + + DrawText(p, center + QPoint(0, -58), 4.7f, tr("START/PAUSE")); + + // Draw right joystick body + p.setBrush(colors.button); + DrawCircle(p, center + QPointF(61, 37), 23.5f); + + // Draw joystick details + p.setBrush(colors.transparent); + DrawPolygon(p, left_hex); + DrawPolygon(p, right_hex); +} + +void PlayerControlPreview::DrawHandheldBody(QPainter& p, const QPointF center) { + const std::size_t body_outline_end = handheld_body.size() / 2 - 6; + const std::size_t bezel_outline_end = handheld_bezel.size() / 2 - 6; + const std::size_t bezel_inline_size = 4; + const std::size_t bezel_inline_start = 35; + std::array<QPointF, left_joycon_body.size() / 2> left_joycon; + std::array<QPointF, left_joycon_body.size() / 2> right_joycon; + std::array<QPointF, handheld_body.size() / 2> qhandheld_body; + std::array<QPointF, body_outline_end> qhandheld_body_outline; + std::array<QPointF, handheld_bezel.size() / 2> qhandheld_bezel; + std::array<QPointF, bezel_inline_size> qhandheld_bezel_inline; + std::array<QPointF, bezel_outline_end> qhandheld_bezel_outline; + std::array<QPointF, handheld_buttons.size() / 2> qhandheld_buttons; + + for (std::size_t point = 0; point < left_joycon_body.size() / 2; ++point) { + left_joycon[point] = + center + QPointF(left_joycon_body[point * 2], left_joycon_body[point * 2 + 1]); + right_joycon[point] = + center + QPointF(-left_joycon_body[point * 2], left_joycon_body[point * 2 + 1]); + } + for (std::size_t point = 0; point < body_outline_end; ++point) { + qhandheld_body_outline[point] = + center + QPointF(handheld_body[point * 2], handheld_body[point * 2 + 1]); + } + for (std::size_t point = 0; point < handheld_body.size() / 2; ++point) { + qhandheld_body[point] = + center + QPointF(handheld_body[point * 2], handheld_body[point * 2 + 1]); + } + for (std::size_t point = 0; point < handheld_bezel.size() / 2; ++point) { + qhandheld_bezel[point] = + center + QPointF(handheld_bezel[point * 2], handheld_bezel[point * 2 + 1]); + } + for (std::size_t point = 0; point < bezel_outline_end; ++point) { + qhandheld_bezel_outline[point] = + center + QPointF(handheld_bezel[point * 2], handheld_bezel[point * 2 + 1]); + } + for (std::size_t point = 0; point < bezel_inline_size; ++point) { + qhandheld_bezel_inline[point] = + center + QPointF(handheld_bezel[(point + bezel_inline_start) * 2], + handheld_bezel[(point + bezel_inline_start) * 2 + 1]); + } + for (std::size_t point = 0; point < handheld_buttons.size() / 2; ++point) { + qhandheld_buttons[point] = + center + QPointF(handheld_buttons[point * 2], handheld_buttons[point * 2 + 1]); + } + + // Draw left joycon + p.setPen(colors.outline); + p.setBrush(colors.left); + DrawPolygon(p, left_joycon); + + // Draw right joycon + p.setPen(colors.outline); + p.setBrush(colors.right); + DrawPolygon(p, right_joycon); + + // Draw Handheld buttons + p.setPen(colors.outline); + p.setBrush(colors.button); + DrawPolygon(p, qhandheld_buttons); + + // Draw handheld body + p.setPen(colors.transparent); + p.setBrush(colors.primary); + DrawPolygon(p, qhandheld_body); + p.setPen(colors.outline); + p.setBrush(colors.transparent); + DrawPolygon(p, qhandheld_body_outline); + + // Draw Handheld bezel + p.setPen(colors.transparent); + p.setBrush(colors.button); + DrawPolygon(p, qhandheld_bezel); + p.setPen(colors.outline); + p.setBrush(colors.transparent); + DrawPolygon(p, qhandheld_bezel_outline); + DrawPolygon(p, qhandheld_bezel_inline); +} + +void PlayerControlPreview::DrawDualBody(QPainter& p, const QPointF center) { + std::array<QPointF, left_joycon_body.size() / 2> left_joycon; + std::array<QPointF, left_joycon_body.size() / 2> right_joycon; + std::array<QPointF, left_joycon_slider.size() / 2> qleft_joycon_slider; + std::array<QPointF, left_joycon_slider.size() / 2> qright_joycon_slider; + std::array<QPointF, left_joycon_slider_topview.size() / 2> qleft_joycon_slider_topview; + std::array<QPointF, left_joycon_slider_topview.size() / 2> qright_joycon_slider_topview; + std::array<QPointF, left_joycon_topview.size() / 2> qleft_joycon_topview; + std::array<QPointF, left_joycon_topview.size() / 2> qright_joycon_topview; + constexpr float size = 1.61f; + constexpr float size2 = 0.9f; + constexpr float offset = 209.3f; + + for (std::size_t point = 0; point < left_joycon_body.size() / 2; ++point) { + left_joycon[point] = center + QPointF(left_joycon_body[point * 2] * size + offset, + left_joycon_body[point * 2 + 1] * size - 1); + right_joycon[point] = center + QPointF(-left_joycon_body[point * 2] * size - offset, + left_joycon_body[point * 2 + 1] * size - 1); + } + for (std::size_t point = 0; point < left_joycon_slider.size() / 2; ++point) { + qleft_joycon_slider[point] = + center + QPointF(left_joycon_slider[point * 2], left_joycon_slider[point * 2 + 1]); + qright_joycon_slider[point] = + center + QPointF(-left_joycon_slider[point * 2], left_joycon_slider[point * 2 + 1]); + } + for (std::size_t point = 0; point < left_joycon_topview.size() / 2; ++point) { + qleft_joycon_topview[point] = + center + QPointF(left_joycon_topview[point * 2] * size2 - 52, + left_joycon_topview[point * 2 + 1] * size2 - 52); + qright_joycon_topview[point] = + center + QPointF(-left_joycon_topview[point * 2] * size2 + 52, + left_joycon_topview[point * 2 + 1] * size2 - 52); + } + for (std::size_t point = 0; point < left_joycon_slider_topview.size() / 2; ++point) { + qleft_joycon_slider_topview[point] = + center + QPointF(left_joycon_slider_topview[point * 2] * size2 - 52, + left_joycon_slider_topview[point * 2 + 1] * size2 - 52); + qright_joycon_slider_topview[point] = + center + QPointF(-left_joycon_slider_topview[point * 2] * size2 + 52, + left_joycon_slider_topview[point * 2 + 1] * size2 - 52); + } + + // right joycon body + p.setPen(colors.outline); + p.setBrush(colors.right); + DrawPolygon(p, right_joycon); + + // Left joycon body + p.setPen(colors.outline); + p.setBrush(colors.left); + DrawPolygon(p, left_joycon); + + // Slider release button top view + p.setBrush(colors.button); + DrawRoundRectangle(p, center + QPoint(-149, -108), 12, 11, 2); + DrawRoundRectangle(p, center + QPoint(149, -108), 12, 11, 2); + + // Joycon slider top view + p.setBrush(colors.slider); + DrawPolygon(p, qleft_joycon_slider_topview); + p.drawLine(center + QPointF(-133.8f, -99.0f), center + QPointF(-133.8f, -78.5f)); + DrawPolygon(p, qright_joycon_slider_topview); + p.drawLine(center + QPointF(133.8f, -99.0f), center + QPointF(133.8f, -78.5f)); + + // Joycon body top view + p.setBrush(colors.left); + DrawPolygon(p, qleft_joycon_topview); + p.setBrush(colors.right); + DrawPolygon(p, qright_joycon_topview); + + // Right SR and SL sideview buttons + p.setPen(colors.outline); + p.setBrush(colors.slider_button); + DrawRoundRectangle(p, center + QPoint(19, 47), 7, 22, 1); + DrawRoundRectangle(p, center + QPoint(19, -62), 7, 22, 1); + + // Left SR and SL sideview buttons + DrawRoundRectangle(p, center + QPoint(-19, 47), 7, 22, 1); + DrawRoundRectangle(p, center + QPoint(-19, -62), 7, 22, 1); + + // Right Sideview body + p.setBrush(colors.slider); + DrawPolygon(p, qright_joycon_slider); + + // Left Sideview body + p.setBrush(colors.slider); + DrawPolygon(p, qleft_joycon_slider); +} + +void PlayerControlPreview::DrawLeftBody(QPainter& p, const QPointF center) { + std::array<QPointF, left_joycon_body.size() / 2> left_joycon; + std::array<QPointF, left_joycon_sideview.size() / 2> qleft_joycon_sideview; + std::array<QPointF, left_joycon_body_trigger.size() / 2> qleft_joycon_trigger; + std::array<QPointF, left_joycon_slider.size() / 2> qleft_joycon_slider; + std::array<QPointF, left_joycon_slider_topview.size() / 2> qleft_joycon_slider_topview; + std::array<QPointF, left_joycon_topview.size() / 2> qleft_joycon_topview; + constexpr float size = 1.78f; + constexpr float size2 = 1.1115f; + constexpr float offset = 312.39f; + constexpr float offset2 = 335; + + for (std::size_t point = 0; point < left_joycon_body.size() / 2; ++point) { + left_joycon[point] = center + QPointF(left_joycon_body[point * 2] * size + offset, + left_joycon_body[point * 2 + 1] * size - 1); + } + + for (std::size_t point = 0; point < left_joycon_sideview.size() / 2; ++point) { + qleft_joycon_sideview[point] = + center + QPointF(left_joycon_sideview[point * 2] * size2 + offset2, + left_joycon_sideview[point * 2 + 1] * size2 + 2); + } + for (std::size_t point = 0; point < left_joycon_slider.size() / 2; ++point) { + qleft_joycon_slider[point] = center + QPointF(left_joycon_slider[point * 2] * size2 + 81, + left_joycon_slider[point * 2 + 1] * size2); + } + for (std::size_t point = 0; point < left_joycon_body_trigger.size() / 2; ++point) { + qleft_joycon_trigger[point] = + center + QPointF(left_joycon_body_trigger[point * 2] * size2 + offset2, + left_joycon_body_trigger[point * 2 + 1] * size2 + 2); + } + for (std::size_t point = 0; point < left_joycon_topview.size() / 2; ++point) { + qleft_joycon_topview[point] = + center + QPointF(left_joycon_topview[point * 2], left_joycon_topview[point * 2 + 1]); + } + for (std::size_t point = 0; point < left_joycon_slider_topview.size() / 2; ++point) { + qleft_joycon_slider_topview[point] = + center + QPointF(left_joycon_slider_topview[point * 2], + left_joycon_slider_topview[point * 2 + 1]); + } + + // Joycon body + p.setPen(colors.outline); + p.setBrush(colors.left); + DrawPolygon(p, left_joycon); + DrawPolygon(p, qleft_joycon_trigger); + + // Slider release button top view + p.setBrush(colors.button); + DrawRoundRectangle(p, center + QPoint(-107, -62), 14, 12, 2); + + // Joycon slider top view + p.setBrush(colors.slider); + DrawPolygon(p, qleft_joycon_slider_topview); + p.drawLine(center + QPointF(-91.1f, -51.7f), center + QPointF(-91.1f, -26.5f)); + + // Joycon body top view + p.setBrush(colors.left); + DrawPolygon(p, qleft_joycon_topview); + + // Slider release button + p.setBrush(colors.button); + DrawRoundRectangle(p, center + QPoint(175, -110), 12, 14, 2); + + // Sideview body + p.setBrush(colors.left); + DrawPolygon(p, qleft_joycon_sideview); + p.setBrush(colors.slider); + DrawPolygon(p, qleft_joycon_slider); + + const QPointF sideview_center = QPointF(155, 0) + center; + + // Sideview slider body + p.setBrush(colors.slider); + DrawRoundRectangle(p, sideview_center + QPointF(0, -5), 28, 253, 3); + p.setBrush(colors.button2); + DrawRoundRectangle(p, sideview_center + QPointF(0, 97), 22.44f, 44.66f, 3); + + // Slider decorations + p.setPen(colors.outline); + p.setBrush(colors.slider_arrow); + DrawArrow(p, sideview_center + QPoint(0, 83), Direction::Down, 2.2f); + DrawArrow(p, sideview_center + QPoint(0, 96), Direction::Down, 2.2f); + DrawArrow(p, sideview_center + QPoint(0, 109), Direction::Down, 2.2f); + DrawCircle(p, sideview_center + QPointF(0, 19), 4.44f); + + // LED indicators + const float led_size = 5.0f; + const QPointF led_position = sideview_center + QPointF(0, -36); + int led_count = 0; + for (const auto color : led_color) { + p.setBrush(color); + DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); + } +} + +void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) { + std::array<QPointF, left_joycon_body.size() / 2> right_joycon; + std::array<QPointF, left_joycon_sideview.size() / 2> qright_joycon_sideview; + std::array<QPointF, left_joycon_body_trigger.size() / 2> qright_joycon_trigger; + std::array<QPointF, left_joycon_slider.size() / 2> qright_joycon_slider; + std::array<QPointF, left_joycon_slider_topview.size() / 2> qright_joycon_slider_topview; + std::array<QPointF, left_joycon_topview.size() / 2> qright_joycon_topview; + constexpr float size = 1.78f; + constexpr float size2 = 1.1115f; + constexpr float offset = 312.39f; + constexpr float offset2 = 335; + + for (std::size_t point = 0; point < left_joycon_body.size() / 2; ++point) { + right_joycon[point] = center + QPointF(-left_joycon_body[point * 2] * size - offset, + left_joycon_body[point * 2 + 1] * size - 1); + } + + for (std::size_t point = 0; point < left_joycon_sideview.size() / 2; ++point) { + qright_joycon_sideview[point] = + center + QPointF(-left_joycon_sideview[point * 2] * size2 - offset2, + left_joycon_sideview[point * 2 + 1] * size2 + 2); + } + for (std::size_t point = 0; point < left_joycon_body_trigger.size() / 2; ++point) { + qright_joycon_trigger[point] = + center + QPointF(-left_joycon_body_trigger[point * 2] * size2 - offset2, + left_joycon_body_trigger[point * 2 + 1] * size2 + 2); + } + for (std::size_t point = 0; point < left_joycon_slider.size() / 2; ++point) { + qright_joycon_slider[point] = center + QPointF(-left_joycon_slider[point * 2] * size2 - 81, + left_joycon_slider[point * 2 + 1] * size2); + } + for (std::size_t point = 0; point < left_joycon_topview.size() / 2; ++point) { + qright_joycon_topview[point] = + center + QPointF(-left_joycon_topview[point * 2], left_joycon_topview[point * 2 + 1]); + } + for (std::size_t point = 0; point < left_joycon_slider_topview.size() / 2; ++point) { + qright_joycon_slider_topview[point] = + center + QPointF(-left_joycon_slider_topview[point * 2], + left_joycon_slider_topview[point * 2 + 1]); + } + + // Joycon body + p.setPen(colors.outline); + p.setBrush(colors.left); + DrawPolygon(p, right_joycon); + DrawPolygon(p, qright_joycon_trigger); + + // Slider release button top view + p.setBrush(colors.button); + DrawRoundRectangle(p, center + QPoint(107, -62), 14, 12, 2); + + // Joycon slider top view + p.setBrush(colors.slider); + DrawPolygon(p, qright_joycon_slider_topview); + p.drawLine(center + QPointF(91.1f, -51.7f), center + QPointF(91.1f, -26.5f)); + + // Joycon body top view + p.setBrush(colors.left); + DrawPolygon(p, qright_joycon_topview); + + // Slider release button + p.setBrush(colors.button); + DrawRoundRectangle(p, center + QPoint(-175, -110), 12, 14, 2); + + // Sideview body + p.setBrush(colors.left); + DrawPolygon(p, qright_joycon_sideview); + p.setBrush(colors.slider); + DrawPolygon(p, qright_joycon_slider); + + const QPointF sideview_center = QPointF(-155, 0) + center; + + // Sideview slider body + p.setBrush(colors.slider); + DrawRoundRectangle(p, sideview_center + QPointF(0, -5), 28, 253, 3); + p.setBrush(colors.button2); + DrawRoundRectangle(p, sideview_center + QPointF(0, 97), 22.44f, 44.66f, 3); + + // Slider decorations + p.setPen(colors.outline); + p.setBrush(colors.slider_arrow); + DrawArrow(p, sideview_center + QPoint(0, 83), Direction::Down, 2.2f); + DrawArrow(p, sideview_center + QPoint(0, 96), Direction::Down, 2.2f); + DrawArrow(p, sideview_center + QPoint(0, 109), Direction::Down, 2.2f); + DrawCircle(p, sideview_center + QPointF(0, 19), 4.44f); + + // LED indicators + const float led_size = 5.0f; + const QPointF led_position = sideview_center + QPointF(0, -36); + int led_count = 0; + for (const auto color : led_color) { + p.setBrush(color); + DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); + } +} + +void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bool left_pressed, + bool right_pressed) { + std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger; + std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger; + std::array<QPointF, pro_body_top.size()> qbody_top; + + for (std::size_t point = 0; point < pro_left_trigger.size() / 2; ++point) { + qleft_trigger[point] = + center + QPointF(pro_left_trigger[point * 2], + pro_left_trigger[point * 2 + 1] + (left_pressed ? 2 : 0)); + qright_trigger[point] = + center + QPointF(-pro_left_trigger[point * 2], + pro_left_trigger[point * 2 + 1] + (right_pressed ? 2 : 0)); + } + + for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) { + qbody_top[pro_body_top.size() - 1 - point] = + center + QPointF(-pro_body_top[point * 2], pro_body_top[point * 2 + 1]); + qbody_top[point] = center + QPointF(pro_body_top[point * 2], pro_body_top[point * 2 + 1]); + } + + // Pro body detail + p.setPen(colors.outline); + p.setBrush(colors.primary); + DrawPolygon(p, qbody_top); + + // Left trigger + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + + // Right trigger + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); +} + +void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, bool left_pressed, + bool right_pressed) { + std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger; + std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger; + + for (std::size_t point = 0; point < left_gc_trigger.size() / 2; ++point) { + qleft_trigger[point] = + center + QPointF(left_gc_trigger[point * 2], + left_gc_trigger[point * 2 + 1] + (left_pressed ? 10 : 0)); + qright_trigger[point] = + center + QPointF(-left_gc_trigger[point * 2], + left_gc_trigger[point * 2 + 1] + (right_pressed ? 10 : 0)); + } + + // Left trigger + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + + // Right trigger + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); + + // Draw L text + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, center + QPointF(-132, -119 + (left_pressed ? 10 : 0)), Symbol::L, 1.7f); + + // Draw R text + p.setPen(colors.transparent); + p.setBrush(colors.font); + DrawSymbol(p, center + QPointF(121.5f, -119 + (right_pressed ? 10 : 0)), Symbol::R, 1.7f); +} + +void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center, + bool left_pressed, bool right_pressed) { + std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; + std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; + + for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { + qleft_trigger[point] = + center + QPointF(left_joycon_trigger[point * 2], + left_joycon_trigger[point * 2 + 1] + (left_pressed ? 0.5f : 0)); + qright_trigger[point] = + center + QPointF(-left_joycon_trigger[point * 2], + left_joycon_trigger[point * 2 + 1] + (right_pressed ? 0.5f : 0)); + } + + // Left trigger + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + + // Right trigger + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); +} + +void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, bool left_pressed, + bool right_pressed) { + std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; + std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; + constexpr float size = 1.62f; + constexpr float offset = 210.6f; + for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { + qleft_trigger[point] = + center + QPointF(left_joycon_trigger[point * 2] * size + offset, + left_joycon_trigger[point * 2 + 1] * size + (left_pressed ? 0.5f : 0)); + qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset, + left_joycon_trigger[point * 2 + 1] * size + + (right_pressed ? 0.5f : 0)); + } + + // Left trigger + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + + // Right trigger + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); +} + +void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF center, + bool left_pressed, bool right_pressed) { + std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; + std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; + constexpr float size = 0.9f; + + for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { + qleft_trigger[point] = center + QPointF(left_joystick_L_topview[point * 2] * size - 50, + left_joystick_L_topview[point * 2 + 1] * size - 52); + } + for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { + qright_trigger[point] = + center + QPointF(-left_joystick_L_topview[point * 2] * size + 50, + left_joystick_L_topview[point * 2 + 1] * size - 52); + } + + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); + + // Draw L text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(-183, -84), Symbol::L, 1.0f); + + // Draw R text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f); +} + +void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF center, + bool left_pressed, bool right_pressed) { + std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; + std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; + constexpr float size = 0.9f; + + for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { + qleft_trigger[point] = + center + QPointF(left_joystick_ZL_topview[point * 2] * size - 52, + left_joystick_ZL_topview[point * 2 + 1] * size - 52); + } + for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { + qright_trigger[point] = + center + QPointF(-left_joystick_ZL_topview[point * 2] * size + 52, + left_joystick_ZL_topview[point * 2 + 1] * size - 52); + } + + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); + + // Draw ZL text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(-180, -113), Symbol::ZL, 1.0f); + + // Draw ZR text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f); +} + +void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, bool left_pressed) { + std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; + constexpr float size = 1.78f; + constexpr float offset = 311.5f; + + for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { + qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset, + left_joycon_trigger[point * 2 + 1] * size - + (left_pressed ? 0.5f : 1.0f)); + } + + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); +} + +void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center, bool left_pressed) { + std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger; + constexpr float size = 1.1115f; + constexpr float offset2 = 335; + + for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { + qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2, + left_joycon_sideview_zl[point * 2 + 1] * size + + (left_pressed ? 1.5f : 1.0f)); + } + + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + p.drawArc(center.x() + 158, center.y() + (left_pressed ? -203.5f : -204.0f), 77, 77, 225 * 16, + 44 * 16); +} + +void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF center, + bool left_pressed) { + std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; + + for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { + qleft_trigger[point] = center + QPointF(left_joystick_L_topview[point * 2], + left_joystick_L_topview[point * 2 + 1]); + } + + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + + // Draw L text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f); +} + +void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF center, + bool left_pressed) { + std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; + + for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { + qleft_trigger[point] = center + QPointF(left_joystick_ZL_topview[point * 2], + left_joystick_ZL_topview[point * 2 + 1]); + } + + p.setPen(colors.outline); + p.setBrush(left_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qleft_trigger); + + // Draw ZL text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(-140, -68), Symbol::ZL, 1.0f); +} + +void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center, + bool right_pressed) { + std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; + constexpr float size = 1.78f; + constexpr float offset = 311.5f; + + for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { + qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset, + left_joycon_trigger[point * 2 + 1] * size - + (right_pressed ? 0.5f : 1.0f)); + } + + p.setPen(colors.outline); + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); +} + +void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center, + bool right_pressed) { + std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger; + constexpr float size = 1.1115f; + constexpr float offset2 = 335; + + for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { + qright_trigger[point] = + center + + QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2, + left_joycon_sideview_zl[point * 2 + 1] * size + (right_pressed ? 0.5f : 0) + 1); + } + + p.setPen(colors.outline); + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); + p.drawArc(center.x() - 236, center.y() + (right_pressed ? -203.5f : -204.0f), 77, 77, 271 * 16, + 44 * 16); +} + +void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF center, + bool right_pressed) { + std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; + + for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { + qright_trigger[point] = center + QPointF(-left_joystick_L_topview[point * 2], + left_joystick_L_topview[point * 2 + 1]); + } + + p.setPen(colors.outline); + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); + + // Draw R text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f); +} + +void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF center, + bool right_pressed) { + std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; + + for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { + qright_trigger[point] = center + QPointF(-left_joystick_ZL_topview[point * 2], + left_joystick_ZL_topview[point * 2 + 1]); + } + + p.setPen(colors.outline); + p.setBrush(right_pressed ? colors.highlight : colors.button); + DrawPolygon(p, qright_trigger); + + // Draw ZR text + p.setPen(colors.transparent); + p.setBrush(colors.font2); + DrawSymbol(p, center + QPointF(140, -68), Symbol::ZR, 1.0f); +} + +void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size, + bool pressed) { + const float radius1 = 13.0f * size; + const float radius2 = 9.0f * size; + + // Outer circle + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawCircle(p, center, radius1); + + // Cross + p.drawLine(center - QPoint(radius1, 0), center + QPoint(radius1, 0)); + p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1)); + + // Inner circle + p.setBrush(pressed ? colors.highlight2 : colors.button2); + DrawCircle(p, center, radius2); +} + +void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle, + float size, bool pressed) { + QVector<QPointF> joystick; + joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2)); + + for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) { + joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed ? 1 : 0), + left_joystick_sideview[point * 2 + 1] * size - 1)); + } + + // Rotate joystick + QTransform t; + t.translate(center.x(), center.y()); + t.rotate(18 * angle); + QPolygonF p2 = t.map(QPolygonF(joystick)); + + // Draw joystick + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + p.drawPolygon(p2); + p.drawLine(p2.at(1), p2.at(30)); + p.drawLine(p2.at(32), p2.at(71)); +} + +void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, bool pressed) { + // Outer circle + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawCircle(p, center, 24.0f); + + // Inner circle + p.setBrush(pressed ? colors.highlight2 : colors.button2); + DrawCircle(p, center, 17.0f); +} + +void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, bool pressed) { + // Outer circle + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawCircle(p, center, 26.0f); + + // Inner circle + p.setBrush(pressed ? colors.highlight2 : colors.button2); + DrawCircle(p, center, 19.0f); + p.setBrush(colors.transparent); + DrawCircle(p, center, 13.5f); + DrawCircle(p, center, 7.5f); +} + +void PlayerControlPreview::DrawRawJoystick(QPainter& p, const QPointF center, const QPointF value, + const Input::AnalogProperties properties) { + constexpr float size = 45.0f; + const float range = size * properties.range; + const float deadzone = size * properties.deadzone; + + // Max range zone circle + p.setPen(colors.outline); + p.setBrush(colors.transparent); + QPen pen = p.pen(); + pen.setStyle(Qt::DotLine); + p.setPen(pen); + DrawCircle(p, center, range); + + // Deadzone circle + pen.setColor(colors.deadzone); + p.setPen(pen); + DrawCircle(p, center, deadzone); + + // Dot pointer + p.setPen(colors.indicator); + p.setBrush(colors.indicator); + DrawCircle(p, center + (value * range), 2); +} + +void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, + float height, Direction direction, float radius) { + p.setBrush(button_color); + if (pressed) { + switch (direction) { + case Direction::Left: + center.setX(center.x() - 1); + break; + case Direction::Right: + center.setX(center.x() + 1); + break; + case Direction::Down: + center.setY(center.y() + 1); + break; + case Direction::Up: + center.setY(center.y() - 1); + break; + case Direction::None: + break; + } + p.setBrush(colors.highlight); + } + QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; + p.drawRoundedRect(rect, radius, radius); +} +void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, bool pressed, + int button_size) { + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawRectangle(p, center, button_size, button_size / 3.0f); +} +void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, bool pressed, + int button_size) { + // Draw outer line + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawRectangle(p, center, button_size, button_size / 3.0f); + DrawRectangle(p, center, button_size / 3.0f, button_size); + + // Scale down size + button_size *= 0.88f; + + // Draw inner color + p.setPen(colors.transparent); + DrawRectangle(p, center, button_size, button_size / 3.0f); + DrawRectangle(p, center, button_size / 3.0f, button_size); +} + +void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool pressed) { + std::array<QPointF, gc_button_x.size() / 2> button_x; + + for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) { + button_x[point] = center + QPointF(gc_button_x[point * 2], gc_button_x[point * 2 + 1]); + } + + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawPolygon(p, button_x); +} + +void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool pressed) { + std::array<QPointF, gc_button_y.size() / 2> button_x; + + for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) { + button_x[point] = center + QPointF(gc_button_y[point * 2], gc_button_y[point * 2 + 1]); + } + + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawPolygon(p, button_x); +} + +void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center, bool pressed) { + std::array<QPointF, gc_button_z.size() / 2> button_x; + + for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) { + button_x[point] = center + QPointF(gc_button_z[point * 2], + gc_button_z[point * 2 + 1] + (pressed ? 1 : 0)); + } + + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button2); + DrawPolygon(p, button_x); +} + +void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, bool pressed, + float button_size) { + p.setBrush(button_color); + if (pressed) { + p.setBrush(colors.highlight); + } + p.drawEllipse(center, button_size, button_size); +} + +void PlayerControlPreview::DrawArrowButtonOutline(QPainter& p, const QPointF center, float size) { + const std::size_t arrow_points = up_arrow_button.size() / 2; + std::array<QPointF, (arrow_points - 1) * 4> arrow_button_outline; + + for (std::size_t point = 0; point < arrow_points - 1; ++point) { + arrow_button_outline[point] = center + QPointF(up_arrow_button[point * 2] * size, + up_arrow_button[point * 2 + 1] * size); + arrow_button_outline[(arrow_points - 1) * 2 - point - 1] = + center + + QPointF(up_arrow_button[point * 2 + 1] * size, up_arrow_button[point * 2] * size); + arrow_button_outline[(arrow_points - 1) * 2 + point] = + center + + QPointF(-up_arrow_button[point * 2] * size, -up_arrow_button[point * 2 + 1] * size); + arrow_button_outline[(arrow_points - 1) * 4 - point - 1] = + center + + QPointF(-up_arrow_button[point * 2 + 1] * size, -up_arrow_button[point * 2] * size); + } + // Draw arrow button outline + p.setPen(colors.outline); + p.setBrush(colors.transparent); + DrawPolygon(p, arrow_button_outline); +} + +void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, + const Direction direction, bool pressed, float size) { + std::array<QPointF, up_arrow_button.size() / 2> arrow_button; + QPoint offset; + + for (std::size_t point = 0; point < up_arrow_button.size() / 2; ++point) { + switch (direction) { + case Direction::Up: + arrow_button[point] = center + QPointF(up_arrow_button[point * 2] * size, + up_arrow_button[point * 2 + 1] * size); + break; + case Direction::Left: + arrow_button[point] = center + QPointF(up_arrow_button[point * 2 + 1] * size, + up_arrow_button[point * 2] * size); + break; + case Direction::Right: + arrow_button[point] = center + QPointF(-up_arrow_button[point * 2 + 1] * size, + up_arrow_button[point * 2] * size); + break; + case Direction::Down: + arrow_button[point] = center + QPointF(up_arrow_button[point * 2] * size, + -up_arrow_button[point * 2 + 1] * size); + break; + case Direction::None: + break; + } + } + + // Draw arrow button + p.setPen(pressed ? colors.highlight : colors.button); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawPolygon(p, arrow_button); + + switch (direction) { + case Direction::Up: + offset = QPoint(0, -20 * size); + break; + case Direction::Left: + offset = QPoint(-20 * size, 0); + break; + case Direction::Right: + offset = QPoint(20 * size, 0); + break; + case Direction::Down: + offset = QPoint(0, 20 * size); + break; + case Direction::None: + offset = QPoint(0, 0); + break; + } + + // Draw arrow icon + p.setPen(colors.font2); + p.setBrush(colors.font2); + DrawArrow(p, center + offset, direction, size); +} + +void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center, + const Direction direction, bool pressed) { + std::array<QPointF, trigger_button.size() / 2> qtrigger_button; + QPoint offset; + + for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) { + switch (direction) { + case Direction::Left: + qtrigger_button[point] = + center + QPointF(-trigger_button[point * 2], trigger_button[point * 2 + 1]); + break; + case Direction::Right: + qtrigger_button[point] = + center + QPointF(trigger_button[point * 2], trigger_button[point * 2 + 1]); + break; + case Direction::Up: + case Direction::Down: + case Direction::None: + break; + } + } + + // Draw arrow button + p.setPen(colors.outline); + p.setBrush(pressed ? colors.highlight : colors.button); + DrawPolygon(p, qtrigger_button); +} + +void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol, + float icon_size) { + std::array<QPointF, house.size() / 2> house_icon; + std::array<QPointF, symbol_a.size() / 2> a_icon; + std::array<QPointF, symbol_b.size() / 2> b_icon; + std::array<QPointF, symbol_x.size() / 2> x_icon; + std::array<QPointF, symbol_y.size() / 2> y_icon; + std::array<QPointF, symbol_l.size() / 2> l_icon; + std::array<QPointF, symbol_r.size() / 2> r_icon; + std::array<QPointF, symbol_c.size() / 2> c_icon; + std::array<QPointF, symbol_zl.size() / 2> zl_icon; + std::array<QPointF, symbol_sl.size() / 2> sl_icon; + std::array<QPointF, symbol_zr.size() / 2> zr_icon; + std::array<QPointF, symbol_sr.size() / 2> sr_icon; + switch (symbol) { + case Symbol::House: + for (std::size_t point = 0; point < house.size() / 2; ++point) { + house_icon[point] = center + QPointF(house[point * 2] * icon_size, + (house[point * 2 + 1] - 0.025f) * icon_size); + } + p.drawPolygon(house_icon.data(), static_cast<int>(house_icon.size())); + break; + case Symbol::A: + for (std::size_t point = 0; point < symbol_a.size() / 2; ++point) { + a_icon[point] = center + QPointF(symbol_a[point * 2] * icon_size, + symbol_a[point * 2 + 1] * icon_size); + } + p.drawPolygon(a_icon.data(), static_cast<int>(a_icon.size())); + break; + case Symbol::B: + for (std::size_t point = 0; point < symbol_b.size() / 2; ++point) { + b_icon[point] = center + QPointF(symbol_b[point * 2] * icon_size, + symbol_b[point * 2 + 1] * icon_size); + } + p.drawPolygon(b_icon.data(), static_cast<int>(b_icon.size())); + break; + case Symbol::X: + for (std::size_t point = 0; point < symbol_x.size() / 2; ++point) { + x_icon[point] = center + QPointF(symbol_x[point * 2] * icon_size, + symbol_x[point * 2 + 1] * icon_size); + } + p.drawPolygon(x_icon.data(), static_cast<int>(x_icon.size())); + break; + case Symbol::Y: + for (std::size_t point = 0; point < symbol_y.size() / 2; ++point) { + y_icon[point] = center + QPointF(symbol_y[point * 2] * icon_size, + (symbol_y[point * 2 + 1] - 1.0f) * icon_size); + } + p.drawPolygon(y_icon.data(), static_cast<int>(y_icon.size())); + break; + case Symbol::L: + for (std::size_t point = 0; point < symbol_l.size() / 2; ++point) { + l_icon[point] = center + QPointF(symbol_l[point * 2] * icon_size, + (symbol_l[point * 2 + 1] - 1.0f) * icon_size); + } + p.drawPolygon(l_icon.data(), static_cast<int>(l_icon.size())); + break; + case Symbol::R: + for (std::size_t point = 0; point < symbol_r.size() / 2; ++point) { + r_icon[point] = center + QPointF(symbol_r[point * 2] * icon_size, + (symbol_r[point * 2 + 1] - 1.0f) * icon_size); + } + p.drawPolygon(r_icon.data(), static_cast<int>(r_icon.size())); + break; + case Symbol::C: + for (std::size_t point = 0; point < symbol_c.size() / 2; ++point) { + c_icon[point] = center + QPointF(symbol_c[point * 2] * icon_size, + (symbol_c[point * 2 + 1] - 1.0f) * icon_size); + } + p.drawPolygon(c_icon.data(), static_cast<int>(c_icon.size())); + break; + case Symbol::ZL: + for (std::size_t point = 0; point < symbol_zl.size() / 2; ++point) { + zl_icon[point] = center + QPointF(symbol_zl[point * 2] * icon_size, + symbol_zl[point * 2 + 1] * icon_size); + } + p.drawPolygon(zl_icon.data(), static_cast<int>(zl_icon.size())); + break; + case Symbol::SL: + for (std::size_t point = 0; point < symbol_sl.size() / 2; ++point) { + sl_icon[point] = center + QPointF(symbol_sl[point * 2] * icon_size, + symbol_sl[point * 2 + 1] * icon_size); + } + p.drawPolygon(sl_icon.data(), static_cast<int>(sl_icon.size())); + break; + case Symbol::ZR: + for (std::size_t point = 0; point < symbol_zr.size() / 2; ++point) { + zr_icon[point] = center + QPointF(symbol_zr[point * 2] * icon_size, + symbol_zr[point * 2 + 1] * icon_size); + } + p.drawPolygon(zr_icon.data(), static_cast<int>(zr_icon.size())); + break; + case Symbol::SR: + for (std::size_t point = 0; point < symbol_sr.size() / 2; ++point) { + sr_icon[point] = center + QPointF(symbol_sr[point * 2] * icon_size, + symbol_sr[point * 2 + 1] * icon_size); + } + p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size())); + break; + } +} + +void PlayerControlPreview::DrawArrow(QPainter& p, const QPointF center, const Direction direction, + float size) { + + std::array<QPointF, up_arrow_symbol.size() / 2> arrow_symbol; + + for (std::size_t point = 0; point < up_arrow_symbol.size() / 2; ++point) { + switch (direction) { + case Direction::Up: + arrow_symbol[point] = center + QPointF(up_arrow_symbol[point * 2] * size, + up_arrow_symbol[point * 2 + 1] * size); + break; + case Direction::Left: + arrow_symbol[point] = center + QPointF(up_arrow_symbol[point * 2 + 1] * size, + up_arrow_symbol[point * 2] * size); + break; + case Direction::Right: + arrow_symbol[point] = center + QPointF(-up_arrow_symbol[point * 2 + 1] * size, + up_arrow_symbol[point * 2] * size); + break; + case Direction::Down: + arrow_symbol[point] = center + QPointF(up_arrow_symbol[point * 2] * size, + -up_arrow_symbol[point * 2 + 1] * size); + break; + case Direction::None: + break; + } + } + + DrawPolygon(p, arrow_symbol); +} + +template <size_t N> +void PlayerControlPreview::DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon) { + p.drawPolygon(polygon.data(), static_cast<int>(polygon.size())); +} + +void PlayerControlPreview::DrawCircle(QPainter& p, const QPointF center, float size) { + p.drawEllipse(center, size, size); +} + +void PlayerControlPreview::DrawRectangle(QPainter& p, const QPointF center, float width, + float height) { + const QRectF rect = QRectF(center.x() - (width / 2), center.y() - (height / 2), width, height); + p.drawRect(rect); +} +void PlayerControlPreview::DrawRoundRectangle(QPainter& p, const QPointF center, float width, + float height, float round) { + const QRectF rect = QRectF(center.x() - (width / 2), center.y() - (height / 2), width, height); + p.drawRoundedRect(rect, round, round); +} + +void PlayerControlPreview::DrawText(QPainter& p, const QPointF center, float text_size, + const QString& text) { + SetTextFont(p, text_size); + const QFontMetrics fm(p.font()); + const QPointF offset = {fm.horizontalAdvance(text) / 2.0f, -text_size / 2.0f}; + p.drawText(center - offset, text); +} + +void PlayerControlPreview::SetTextFont(QPainter& p, float text_size, const QString& font_family) { + QFont font = p.font(); + font.setPointSizeF(text_size); + font.setFamily(font_family); + p.setFont(font); +} diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h new file mode 100644 index 000000000..39565f795 --- /dev/null +++ b/src/yuzu/configuration/configure_input_player_widget.h @@ -0,0 +1,192 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <QFrame> +#include <QPointer> +#include "core/frontend/input.h" +#include "core/settings.h" + +class QLabel; + +using AnalogParam = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; +using ButtonParam = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; + +// Widget for representing controller animations +class PlayerControlPreview : public QFrame { + Q_OBJECT + +public: + explicit PlayerControlPreview(QWidget* parent); + ~PlayerControlPreview() override; + + void SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, + const AnalogParam& analogs_param); + void SetPlayerInputRaw(std::size_t index, const Settings::ButtonsRaw buttons_, + Settings::AnalogsRaw analogs_); + void SetConnectedStatus(bool checked); + void SetControllerType(Settings::ControllerType type); + void BeginMappingButton(std::size_t button_id); + void BeginMappingAnalog(std::size_t button_id); + void EndMapping(); + void UpdateInput(); + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + enum class Direction : std::size_t { + None, + Up, + Right, + Down, + Left, + }; + + enum class Symbol { + House, + A, + B, + X, + Y, + L, + R, + C, + SL, + ZL, + ZR, + SR, + }; + + struct AxisValue { + QPointF value{}; + QPointF raw_value{}; + Input::AnalogProperties properties{}; + int size{}; + QPoint offset{}; + bool active{}; + }; + + struct LedPattern { + bool position1; + bool position2; + bool position3; + bool position4; + }; + + struct ColorMapping { + QColor outline{}; + QColor primary{}; + QColor left{}; + QColor right{}; + QColor button{}; + QColor button2{}; + QColor font{}; + QColor font2{}; + QColor highlight{}; + QColor highlight2{}; + QColor transparent{}; + QColor indicator{}; + QColor led_on{}; + QColor led_off{}; + QColor slider{}; + QColor slider_button{}; + QColor slider_arrow{}; + QColor deadzone{}; + }; + + static LedPattern GetColorPattern(std::size_t index, bool player_on); + void UpdateColors(); + + // Draw controller functions + void DrawHandheldController(QPainter& p, QPointF center); + void DrawDualController(QPainter& p, QPointF center); + void DrawLeftController(QPainter& p, QPointF center); + void DrawRightController(QPainter& p, QPointF center); + void DrawProController(QPainter& p, QPointF center); + void DrawGCController(QPainter& p, QPointF center); + + // Draw body functions + void DrawHandheldBody(QPainter& p, QPointF center); + void DrawDualBody(QPainter& p, QPointF center); + void DrawLeftBody(QPainter& p, QPointF center); + void DrawRightBody(QPainter& p, QPointF center); + void DrawProBody(QPainter& p, QPointF center); + void DrawGCBody(QPainter& p, QPointF center); + + // Draw triggers functions + void DrawProTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); + void DrawGCTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); + void DrawHandheldTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); + void DrawDualTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); + void DrawDualTriggersTopView(QPainter& p, QPointF center, bool left_pressed, + bool right_pressed); + void DrawDualZTriggersTopView(QPainter& p, QPointF center, bool left_pressed, + bool right_pressed); + void DrawLeftTriggers(QPainter& p, QPointF center, bool left_pressed); + void DrawLeftZTriggers(QPainter& p, QPointF center, bool left_pressed); + void DrawLeftTriggersTopView(QPainter& p, QPointF center, bool left_pressed); + void DrawLeftZTriggersTopView(QPainter& p, QPointF center, bool left_pressed); + void DrawRightTriggers(QPainter& p, QPointF center, bool right_pressed); + void DrawRightZTriggers(QPainter& p, QPointF center, bool right_pressed); + void DrawRightTriggersTopView(QPainter& p, QPointF center, bool right_pressed); + void DrawRightZTriggersTopView(QPainter& p, QPointF center, bool right_pressed); + + // Draw joystick functions + void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed); + void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed); + void DrawRawJoystick(QPainter& p, QPointF center, const QPointF value, + const Input::AnalogProperties properties); + void DrawProJoystick(QPainter& p, QPointF center, bool pressed); + void DrawGCJoystick(QPainter& p, QPointF center, bool pressed); + + // Draw button functions + void DrawCircleButton(QPainter& p, QPointF center, bool pressed, float button_size); + void DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, float height, + Direction direction = Direction::None, float radius = 2); + void DrawMinusButton(QPainter& p, QPointF center, bool pressed, int button_size); + void DrawPlusButton(QPainter& p, QPointF center, bool pressed, int button_size); + void DrawGCButtonX(QPainter& p, QPointF center, bool pressed); + void DrawGCButtonY(QPainter& p, QPointF center, bool pressed); + void DrawGCButtonZ(QPainter& p, QPointF center, bool pressed); + void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f); + void DrawArrowButton(QPainter& p, QPointF center, Direction direction, bool pressed, + float size = 1.0f); + void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, bool pressed); + + // Draw icon functions + void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size); + void DrawArrow(QPainter& p, QPointF center, Direction direction, float size); + + // Draw primitive types + template <size_t N> + void DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon); + void DrawCircle(QPainter& p, QPointF center, float size); + void DrawRectangle(QPainter& p, QPointF center, float width, float height); + void DrawRoundRectangle(QPainter& p, QPointF center, float width, float height, float round); + void DrawText(QPainter& p, QPointF center, float text_size, const QString& text); + void SetTextFont(QPainter& p, float text_size, + const QString& font_family = QStringLiteral("sans-serif")); + + using ButtonArray = + std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::BUTTON_NS_END>; + using StickArray = + std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>; + + bool mapping_active{}; + int blink_counter{}; + QColor button_color{}; + ColorMapping colors{}; + std::array<QColor, 4> led_color{}; + ButtonArray buttons{}; + StickArray sticks{}; + std::size_t player_index{}; + std::size_t button_mapping_index{Settings::NativeButton::BUTTON_NS_END}; + std::size_t analog_mapping_index{Settings::NativeAnalog::NUM_STICKS_HID}; + std::array<AxisValue, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{}; + std::array<bool, Settings::NativeButton::NumButtons> button_values{}; + Settings::ControllerType controller_type{Settings::ControllerType::ProController}; +}; diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index caaa85930..1f2b792e4 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -81,19 +81,11 @@ void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) { cancel_button->setText(text); } -constexpr std::array<std::pair<const char*, const char*>, 2> TouchProviders = {{ - {"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")}, - {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}, -}}; - ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) : QDialog(parent), input_subsystem{input_subsystem_}, ui(std::make_unique<Ui::ConfigureMotionTouch>()) { ui->setupUi(this); - for (const auto& [provider, name] : TouchProviders) { - ui->touch_provider->addItem(tr(name), QString::fromUtf8(provider)); - } ui->udp_learn_more->setOpenExternalLinks(true); ui->udp_learn_more->setText( @@ -112,10 +104,7 @@ ConfigureMotionTouch::~ConfigureMotionTouch() = default; void ConfigureMotionTouch::SetConfiguration() { const Common::ParamPackage motion_param(Settings::values.motion_device); const Common::ParamPackage touch_param(Settings::values.touch_device); - const std::string touch_engine = touch_param.Get("engine", "emu_window"); - ui->touch_provider->setCurrentIndex( - ui->touch_provider->findData(QString::fromStdString(touch_engine))); ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button); touch_from_button_maps = Settings::values.touch_from_button_maps; for (const auto& touch_map : touch_from_button_maps) { @@ -148,30 +137,21 @@ void ConfigureMotionTouch::SetConfiguration() { } void ConfigureMotionTouch::UpdateUiDisplay() { - const QString touch_engine = ui->touch_provider->currentData().toString(); const QString cemuhook_udp = QStringLiteral("cemuhookudp"); ui->motion_sensitivity_label->setVisible(true); ui->motion_sensitivity->setVisible(true); - if (touch_engine == cemuhook_udp) { - ui->touch_calibration->setVisible(true); - ui->touch_calibration_config->setVisible(true); - ui->touch_calibration_label->setVisible(true); - ui->touch_calibration->setText( - QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y)); - } else { - ui->touch_calibration->setVisible(false); - ui->touch_calibration_config->setVisible(false); - ui->touch_calibration_label->setVisible(false); - } + ui->touch_calibration->setVisible(true); + ui->touch_calibration_config->setVisible(true); + ui->touch_calibration_label->setVisible(true); + ui->touch_calibration->setText( + QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y)); ui->udp_config_group_box->setVisible(true); } void ConfigureMotionTouch::ConnectEvents() { - connect(ui->touch_provider, qOverload<int>(&QComboBox::currentIndexChanged), this, - [this](int index) { UpdateUiDisplay(); }); connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest); connect(ui->udp_add, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPAddServer); connect(ui->udp_remove, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPDeleteServer); @@ -327,16 +307,11 @@ void ConfigureMotionTouch::ApplyConfiguration() { return; } - std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); - Common::ParamPackage touch_param{}; - if (touch_engine == "cemuhookudp") { - touch_param.Set("min_x", min_x); - touch_param.Set("min_y", min_y); - touch_param.Set("max_x", max_x); - touch_param.Set("max_y", max_y); - } - touch_param.Set("engine", std::move(touch_engine)); + touch_param.Set("min_x", min_x); + touch_param.Set("min_y", min_y); + touch_param.Set("max_x", max_x); + touch_param.Set("max_y", max_y); Settings::values.touch_device = touch_param.Serialize(); Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui index ebca835ac..1e35ea946 100644 --- a/src/yuzu/configuration/configure_motion_touch.ui +++ b/src/yuzu/configuration/configure_motion_touch.ui @@ -68,23 +68,9 @@ <item> <layout class="QHBoxLayout"> <item> - <widget class="QLabel" name="touch_provider_label"> - <property name="text"> - <string>Touch Provider:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="touch_provider"/> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout"> - <item> <widget class="QLabel" name="touch_calibration_label"> <property name="text"> - <string>Calibration:</string> + <string>UDP Calibration:</string> </property> </widget> </item> diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 13d9a4757..51647a028 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -40,7 +40,7 @@ QString GetImagePath(Common::UUID uuid) { } QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) { - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!manager.GetProfileBase(uuid, profile)) { return {}; } @@ -116,8 +116,8 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent) scene = new QGraphicsScene; ui->current_user_icon->setScene(scene); - SetConfiguration(); RetranslateUI(); + SetConfiguration(); } ConfigureProfileManager::~ConfigureProfileManager() = default; @@ -147,7 +147,7 @@ void ConfigureProfileManager::SetConfiguration() { void ConfigureProfileManager::PopulateUserList() { const auto& profiles = profile_manager->GetAllUsers(); for (const auto& user : profiles) { - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!profile_manager->GetProfileBase(user, profile)) continue; @@ -212,7 +212,7 @@ void ConfigureProfileManager::RenameUser() { const auto uuid = profile_manager->GetUser(user); ASSERT(uuid); - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!profile_manager->GetProfileBase(*uuid, profile)) return; diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp index 0de7a4f0b..b580cfff2 100644 --- a/src/yuzu/configuration/configure_service.cpp +++ b/src/yuzu/configuration/configure_service.cpp @@ -9,6 +9,7 @@ #include "ui_configure_service.h" #include "yuzu/configuration/configure_service.h" +#ifdef YUZU_ENABLE_BOXCAT namespace { QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { QString out; @@ -32,6 +33,7 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { return out; } } // Anonymous namespace +#endif ConfigureService::ConfigureService(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp index 7d7cc00b7..29c86c7bc 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp +++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp @@ -33,21 +33,18 @@ void ConfigureTouchscreenAdvanced::RetranslateUI() { } void ConfigureTouchscreenAdvanced::ApplyConfiguration() { - Settings::values.touchscreen.finger = ui->finger_box->value(); Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value(); Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value(); Settings::values.touchscreen.rotation_angle = ui->angle_box->value(); } void ConfigureTouchscreenAdvanced::LoadConfiguration() { - ui->finger_box->setValue(Settings::values.touchscreen.finger); ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x); ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y); ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle); } void ConfigureTouchscreenAdvanced::RestoreDefaults() { - ui->finger_box->setValue(0); ui->diameter_x_box->setValue(15); ui->diameter_y_box->setValue(15); ui->angle_box->setValue(0); diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui index 30ceccddb..88e7cf050 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.ui +++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui @@ -65,20 +65,13 @@ </property> </spacer> </item> - <item row="2" column="1"> + <item row="1" column="1"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Touch Diameter Y</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Finger</string> - </property> - </widget> - </item> <item row="0" column="3"> <spacer name="horizontalSpacer_2"> <property name="orientation"> @@ -92,37 +85,27 @@ </property> </spacer> </item> - <item row="1" column="1"> + <item row="0" column="1"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Touch Diameter X</string> </property> </widget> </item> - <item row="0" column="2"> - <widget class="QSpinBox" name="finger_box"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="3" column="1"> + <item row="2" column="1"> <widget class="QLabel" name="label_5"> <property name="text"> <string>Rotational Angle</string> </property> </widget> </item> - <item row="1" column="2"> + <item row="0" column="2"> <widget class="QSpinBox" name="diameter_x_box"/> </item> - <item row="2" column="2"> + <item row="1" column="2"> <widget class="QSpinBox" name="diameter_y_box"/> </item> - <item row="3" column="2"> + <item row="2" column="2"> <widget class="QSpinBox" name="angle_box"/> </item> </layout> diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp new file mode 100644 index 000000000..85724a8f3 --- /dev/null +++ b/src/yuzu/debugger/controller.cpp @@ -0,0 +1,66 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QAction> +#include <QLayout> +#include <QString> +#include "core/settings.h" +#include "yuzu/configuration/configure_input_player_widget.h" +#include "yuzu/debugger/controller.h" + +ControllerDialog::ControllerDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) { + setObjectName(QStringLiteral("Controller")); + setWindowTitle(tr("Controller P1")); + resize(500, 350); + setMinimumSize(500, 350); + // Remove the "?" button from the titlebar and enable the maximize button + setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | + Qt::WindowMaximizeButtonHint); + + widget = new PlayerControlPreview(this); + refreshConfiguration(); + QLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(widget); + setLayout(layout); + + // Configure focus so that widget is focusable and the dialog automatically forwards focus to + // it. + setFocusProxy(widget); + widget->setFocusPolicy(Qt::StrongFocus); + widget->setFocus(); +} + +void ControllerDialog::refreshConfiguration() { + const auto& players = Settings::values.players.GetValue(); + constexpr std::size_t player = 0; + widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs); + widget->SetConnectedStatus(players[player].connected); + widget->SetControllerType(players[player].controller_type); +} + +QAction* ControllerDialog::toggleViewAction() { + if (toggle_view_action == nullptr) { + toggle_view_action = new QAction(windowTitle(), this); + toggle_view_action->setCheckable(true); + toggle_view_action->setChecked(isVisible()); + connect(toggle_view_action, &QAction::toggled, this, &ControllerDialog::setVisible); + } + + return toggle_view_action; +} + +void ControllerDialog::showEvent(QShowEvent* ev) { + if (toggle_view_action) { + toggle_view_action->setChecked(isVisible()); + } + QWidget::showEvent(ev); +} + +void ControllerDialog::hideEvent(QHideEvent* ev) { + if (toggle_view_action) { + toggle_view_action->setChecked(isVisible()); + } + QWidget::hideEvent(ev); +} diff --git a/src/yuzu/debugger/controller.h b/src/yuzu/debugger/controller.h new file mode 100644 index 000000000..c54750070 --- /dev/null +++ b/src/yuzu/debugger/controller.h @@ -0,0 +1,31 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QWidget> + +class QAction; +class QHideEvent; +class QShowEvent; +class PlayerControlPreview; + +class ControllerDialog : public QWidget { + Q_OBJECT + +public: + explicit ControllerDialog(QWidget* parent = nullptr); + + /// Returns a QAction that can be used to toggle visibility of this dialog. + QAction* toggleViewAction(); + void refreshConfiguration(); + +protected: + void showEvent(QShowEvent* ev) override; + void hideEvent(QHideEvent* ev) override; + +private: + QAction* toggle_view_action = nullptr; + PlayerControlPreview* widget; +}; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index a93b5d3c2..3bca6277b 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -13,12 +13,13 @@ #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/svc_common.h" -#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/svc_types.h" #include "core/memory.h" namespace { @@ -90,9 +91,9 @@ std::size_t WaitTreeItem::Row() const { std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { std::vector<std::unique_ptr<WaitTreeThread>> item_list; std::size_t row = 0; - auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { + auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::KThread>>& threads) { for (std::size_t i = 0; i < threads.size(); ++i) { - if (!threads[i]->IsHLEThread()) { + if (threads[i]->GetThreadTypeForDebugging() == Kernel::ThreadType::User) { item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); item_list.back()->row = row; } @@ -117,7 +118,7 @@ WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTa : mutex_address(mutex_address) { mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask); - owner = handle_table.Get<Kernel::Thread>(owner_handle); + owner = handle_table.Get<Kernel::KThread>(owner_handle); } WaitTreeMutexInfo::~WaitTreeMutexInfo() = default; @@ -139,7 +140,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() cons return list; } -WaitTreeCallstack::WaitTreeCallstack(const Kernel::Thread& thread) : thread(thread) {} +WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread) : thread(thread) {} WaitTreeCallstack::~WaitTreeCallstack() = default; QString WaitTreeCallstack::GetText() const { @@ -149,7 +150,7 @@ QString WaitTreeCallstack::GetText() const { std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { std::vector<std::unique_ptr<WaitTreeItem>> list; - if (thread.IsHLEThread()) { + if (thread.GetThreadTypeForDebugging() != Kernel::ThreadType::User) { return list; } @@ -192,9 +193,9 @@ std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::ma const Kernel::KSynchronizationObject& object) { switch (object.GetHandleType()) { case Kernel::HandleType::ReadableEvent: - return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); + return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::KReadableEvent&>(object)); case Kernel::HandleType::Thread: - return std::make_unique<WaitTreeThread>(static_cast<const Kernel::Thread&>(object)); + return std::make_unique<WaitTreeThread>(static_cast<const Kernel::KThread&>(object)); default: return std::make_unique<WaitTreeSynchronizationObject>(object); } @@ -231,21 +232,17 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeObjectList::GetChildren() con return list; } -WaitTreeThread::WaitTreeThread(const Kernel::Thread& thread) +WaitTreeThread::WaitTreeThread(const Kernel::KThread& thread) : WaitTreeSynchronizationObject(thread) {} WaitTreeThread::~WaitTreeThread() = default; QString WaitTreeThread::GetText() const { - const auto& thread = static_cast<const Kernel::Thread&>(object); + const auto& thread = static_cast<const Kernel::KThread&>(object); QString status; switch (thread.GetState()) { case Kernel::ThreadState::Runnable: - if (!thread.IsPaused()) { - if (thread.WasRunning()) { - status = tr("running"); - } else { - status = tr("ready"); - } + if (!thread.IsSuspended()) { + status = tr("runnable"); } else { status = tr("paused"); } @@ -297,15 +294,11 @@ QString WaitTreeThread::GetText() const { QColor WaitTreeThread::GetColor() const { const std::size_t color_index = IsDarkTheme() ? 1 : 0; - const auto& thread = static_cast<const Kernel::Thread&>(object); + const auto& thread = static_cast<const Kernel::KThread&>(object); switch (thread.GetState()) { case Kernel::ThreadState::Runnable: - if (!thread.IsPaused()) { - if (thread.WasRunning()) { - return QColor(WaitTreeColors[0][color_index]); - } else { - return QColor(WaitTreeColors[1][color_index]); - } + if (!thread.IsSuspended()) { + return QColor(WaitTreeColors[0][color_index]); } else { return QColor(WaitTreeColors[2][color_index]); } @@ -336,27 +329,21 @@ QColor WaitTreeThread::GetColor() const { std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeSynchronizationObject::GetChildren()); - const auto& thread = static_cast<const Kernel::Thread&>(object); + const auto& thread = static_cast<const Kernel::KThread&>(object); QString processor; - switch (thread.GetProcessorID()) { - case Kernel::ThreadProcessorId::THREADPROCESSORID_IDEAL: + switch (thread.GetActiveCore()) { + case Kernel::Svc::IdealCoreUseProcessValue: processor = tr("ideal"); break; - case Kernel::ThreadProcessorId::THREADPROCESSORID_0: - case Kernel::ThreadProcessorId::THREADPROCESSORID_1: - case Kernel::ThreadProcessorId::THREADPROCESSORID_2: - case Kernel::ThreadProcessorId::THREADPROCESSORID_3: - processor = tr("core %1").arg(thread.GetProcessorID()); - break; default: - processor = tr("Unknown processor %1").arg(thread.GetProcessorID()); + processor = tr("core %1").arg(thread.GetActiveCore()); break; } list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor))); - list.push_back( - std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.GetIdealCore()))); + list.push_back(std::make_unique<WaitTreeText>( + tr("ideal core = %1").arg(thread.GetIdealCoreForDebugging()))); list.push_back(std::make_unique<WaitTreeText>( tr("affinity mask = %1").arg(thread.GetAffinityMask().GetAffinityMask()))); list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); @@ -386,11 +373,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { return list; } -WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) +WaitTreeEvent::WaitTreeEvent(const Kernel::KReadableEvent& object) : WaitTreeSynchronizationObject(object) {} WaitTreeEvent::~WaitTreeEvent() = default; -WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::Thread*>& list) +WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::KThread*>& list) : thread_list(list) {} WaitTreeThreadList::~WaitTreeThreadList() = default; diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index cf96911ea..3da2fdfd2 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -18,9 +18,9 @@ class EmuThread; namespace Kernel { class HandleTable; +class KReadableEvent; class KSynchronizationObject; -class ReadableEvent; -class Thread; +class KThread; } // namespace Kernel class WaitTreeThread; @@ -83,20 +83,20 @@ private: VAddr mutex_address; u32 mutex_value; Kernel::Handle owner_handle; - std::shared_ptr<Kernel::Thread> owner; + std::shared_ptr<Kernel::KThread> owner; }; class WaitTreeCallstack : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeCallstack(const Kernel::Thread& thread); + explicit WaitTreeCallstack(const Kernel::KThread& thread); ~WaitTreeCallstack() override; QString GetText() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; private: - const Kernel::Thread& thread; + const Kernel::KThread& thread; }; class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { @@ -131,7 +131,7 @@ private: class WaitTreeThread : public WaitTreeSynchronizationObject { Q_OBJECT public: - explicit WaitTreeThread(const Kernel::Thread& thread); + explicit WaitTreeThread(const Kernel::KThread& thread); ~WaitTreeThread() override; QString GetText() const override; @@ -142,21 +142,21 @@ public: class WaitTreeEvent : public WaitTreeSynchronizationObject { Q_OBJECT public: - explicit WaitTreeEvent(const Kernel::ReadableEvent& object); + explicit WaitTreeEvent(const Kernel::KReadableEvent& object); ~WaitTreeEvent() override; }; class WaitTreeThreadList : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeThreadList(const std::vector<Kernel::Thread*>& list); + explicit WaitTreeThreadList(const std::vector<Kernel::KThread*>& list); ~WaitTreeThreadList() override; QString GetText() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; private: - const std::vector<Kernel::Thread*>& thread_list; + const std::vector<Kernel::KThread*>& thread_list; }; class WaitTreeModel : public QAbstractItemModel { diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 37b0d1a0e..9afd5b45f 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -173,8 +173,8 @@ void GameList::OnItemExpanded(const QModelIndex& item) { return; } - auto* game_dir = item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); - game_dir->expanded = tree_view->isExpanded(item); + UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded = + tree_view->isExpanded(item); } // Event in order to filter the gamelist after editing the searchfield @@ -262,9 +262,9 @@ void GameList::OnUpdateThemedIcons() { Qt::DecorationRole); break; case GameListItemType::CustomDir: { - const UISettings::GameDir* game_dir = - child->data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); - const QString icon_name = QFileInfo::exists(game_dir->path) + const UISettings::GameDir& game_dir = + UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; + const QString icon_name = QFileInfo::exists(game_dir.path) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); child->setData( @@ -366,7 +366,7 @@ void GameList::AddDirEntry(GameListDir* entry_items) { item_model->invisibleRootItem()->appendRow(entry_items); tree_view->setExpanded( entry_items->index(), - entry_items->data(GameListDir::GameDirRole).value<UISettings::GameDir*>()->expanded); + UISettings::values.game_dirs[entry_items->data(GameListDir::GameDirRole).toInt()].expanded); } void GameList::AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent) { @@ -549,7 +549,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) { UISettings::GameDir& game_dir = - *selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); + UISettings::values.game_dirs[selected.data(GameListDir::GameDirRole).toInt()]; QAction* deep_scan = context_menu.addAction(tr("Scan Subfolders")); QAction* delete_dir = context_menu.addAction(tr("Remove Game Directory")); @@ -568,8 +568,7 @@ void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) { } void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { - UISettings::GameDir& game_dir = - *selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); + const int game_dir_index = selected.data(GameListDir::GameDirRole).toInt(); QAction* move_up = context_menu.addAction(tr("\u25B2 Move Up")); QAction* move_down = context_menu.addAction(tr("\u25bc Move Down")); @@ -580,34 +579,39 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { move_up->setEnabled(row > 0); move_down->setEnabled(row < item_model->rowCount() - 2); - connect(move_up, &QAction::triggered, [this, selected, row, &game_dir] { - // find the indices of the items in settings and swap them - std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)], - UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf( - *selected.sibling(row - 1, 0) - .data(GameListDir::GameDirRole) - .value<UISettings::GameDir*>())]); + connect(move_up, &QAction::triggered, [this, selected, row, game_dir_index] { + const int other_index = selected.sibling(row - 1, 0).data(GameListDir::GameDirRole).toInt(); + // swap the items in the settings + std::swap(UISettings::values.game_dirs[game_dir_index], + UISettings::values.game_dirs[other_index]); + // swap the indexes held by the QVariants + item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole); + item_model->setData(selected.sibling(row - 1, 0), QVariant(game_dir_index), + GameListDir::GameDirRole); // move the treeview items QList<QStandardItem*> item = item_model->takeRow(row); item_model->invisibleRootItem()->insertRow(row - 1, item); - tree_view->setExpanded(selected, game_dir.expanded); + tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded); }); - connect(move_down, &QAction::triggered, [this, selected, row, &game_dir] { - // find the indices of the items in settings and swap them - std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)], - UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf( - *selected.sibling(row + 1, 0) - .data(GameListDir::GameDirRole) - .value<UISettings::GameDir*>())]); + connect(move_down, &QAction::triggered, [this, selected, row, game_dir_index] { + const int other_index = selected.sibling(row + 1, 0).data(GameListDir::GameDirRole).toInt(); + // swap the items in the settings + std::swap(UISettings::values.game_dirs[game_dir_index], + UISettings::values.game_dirs[other_index]); + // swap the indexes held by the QVariants + item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole); + item_model->setData(selected.sibling(row + 1, 0), QVariant(game_dir_index), + GameListDir::GameDirRole); // move the treeview items const QList<QStandardItem*> item = item_model->takeRow(row); item_model->invisibleRootItem()->insertRow(row + 1, item); - tree_view->setExpanded(selected, game_dir.expanded); + tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded); }); - connect(open_directory_location, &QAction::triggered, - [this, game_dir] { emit OpenDirectory(game_dir.path); }); + connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { + emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); + }); } void GameList::LoadCompatibilityList() { diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index df935022d..f25445f18 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -230,7 +230,7 @@ public: setData(type(), TypeRole); UISettings::GameDir* game_dir = &directory; - setData(QVariant::fromValue(game_dir), GameDirRole); + setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole); const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); switch (dir_type) { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 2e74037d1..ef92c25bc 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -16,6 +16,7 @@ #include "applets/profile_select.h" #include "applets/software_keyboard.h" #include "applets/web_browser.h" +#include "common/nvidia_flags.h" #include "configuration/configure_input.h" #include "configuration/configure_per_game.h" #include "configuration/configure_vibration.h" @@ -108,6 +109,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" #include "yuzu/debugger/console.h" +#include "yuzu/debugger/controller.h" #include "yuzu/debugger/profiler.h" #include "yuzu/debugger/wait_tree.h" #include "yuzu/discord.h" @@ -687,6 +689,11 @@ void GMainWindow::InitializeDebugWidgets() { addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); waitTreeWidget->hide(); debug_menu->addAction(waitTreeWidget->toggleViewAction()); + + controller_dialog = new ControllerDialog(this); + controller_dialog->hide(); + debug_menu->addAction(controller_dialog->toggleViewAction()); + connect(this, &GMainWindow::EmulationStarting, waitTreeWidget, &WaitTreeWidget::OnEmulationStarting); connect(this, &GMainWindow::EmulationStopping, waitTreeWidget, @@ -1038,8 +1045,6 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { std::make_unique<QtWebBrowser>(*this), // Web Browser }); - system.RegisterHostThread(); - const Core::System::ResultStatus result{ system.Load(*render_window, filename.toStdString(), program_index)}; @@ -2337,6 +2342,7 @@ void GMainWindow::OnConfigure() { } configure_dialog.ApplyConfiguration(); + controller_dialog->refreshConfiguration(); InitializeHotkeys(); if (UISettings::values.theme != old_theme) { UpdateUITheme(); @@ -3023,6 +3029,8 @@ int main(int argc, char* argv[]) { MicroProfileOnThreadCreate("Frontend"); SCOPE_EXIT({ MicroProfileShutdown(); }); + Common::ConfigureNvidiaEnvironmentFlags(); + // Init settings params QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); QCoreApplication::setApplicationName(QStringLiteral("yuzu")); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 31788ea62..04d37d4ae 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -27,6 +27,7 @@ class GRenderWindow; class LoadingScreen; class MicroProfileDialog; class ProfilerWidget; +class ControllerDialog; class QLabel; class QPushButton; class QProgressDialog; @@ -313,6 +314,7 @@ private: ProfilerWidget* profilerWidget; MicroProfileDialog* microProfileDialog; WaitTreeWidget* waitTreeWidget; + ControllerDialog* controller_dialog; QAction* actions_recent_files[max_recent_files_item]; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 41ef6f6b8..f76102459 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -296,10 +296,6 @@ void Config::ReadValues() { sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); Settings::values.touchscreen.enabled = sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); - Settings::values.touchscreen.device = - sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window"); - Settings::values.touchscreen.finger = - sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0); Settings::values.touchscreen.rotation_angle = sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); Settings::values.touchscreen.diameter_x = diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index e32bed5e6..7843d5167 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -29,16 +29,16 @@ EmuWindow_SDL2::~EmuWindow_SDL2() { } void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { - TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); + TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); input_subsystem->GetMouse()->MouseMove(x, y); } void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { if (button == SDL_BUTTON_LEFT) { if (state == SDL_PRESSED) { - TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); + TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); } else { - TouchReleased(); + TouchReleased(0); } } else if (button == SDL_BUTTON_RIGHT) { if (state == SDL_PRESSED) { @@ -66,16 +66,16 @@ void EmuWindow_SDL2::OnFingerDown(float x, float y) { // 3DS does const auto [px, py] = TouchToPixelPos(x, y); - TouchPressed(px, py); + TouchPressed(px, py, 0); } void EmuWindow_SDL2::OnFingerMotion(float x, float y) { const auto [px, py] = TouchToPixelPos(x, y); - TouchMoved(px, py); + TouchMoved(px, py, 0); } void EmuWindow_SDL2::OnFingerUp() { - TouchReleased(); + TouchReleased(0); } void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index a103b04bd..deddea9ee 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -59,29 +59,17 @@ private: bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { std::vector<std::string_view> unsupported_ext; - if (!GLAD_GL_ARB_buffer_storage) - unsupported_ext.push_back("ARB_buffer_storage"); - if (!GLAD_GL_ARB_direct_state_access) - unsupported_ext.push_back("ARB_direct_state_access"); - if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) - unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); - if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) - unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); - if (!GLAD_GL_ARB_multi_bind) - unsupported_ext.push_back("ARB_multi_bind"); - if (!GLAD_GL_ARB_clip_control) - unsupported_ext.push_back("ARB_clip_control"); - // Extensions required to support some texture formats. - if (!GLAD_GL_EXT_texture_compression_s3tc) + if (!GLAD_GL_EXT_texture_compression_s3tc) { unsupported_ext.push_back("EXT_texture_compression_s3tc"); - if (!GLAD_GL_ARB_texture_compression_rgtc) + } + if (!GLAD_GL_ARB_texture_compression_rgtc) { unsupported_ext.push_back("ARB_texture_compression_rgtc"); - if (!GLAD_GL_ARB_depth_buffer_float) - unsupported_ext.push_back("ARB_depth_buffer_float"); + } - for (const auto& extension : unsupported_ext) + for (const auto& extension : unsupported_ext) { LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension); + } return unsupported_ext.empty(); } @@ -89,7 +77,7 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen) : EmuWindow_SDL2{input_subsystem} { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 4faf62ede..0e1f3bdb3 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -17,6 +17,7 @@ #include "common/logging/filter.h" #include "common/logging/log.h" #include "common/microprofile.h" +#include "common/nvidia_flags.h" #include "common/scm_rev.h" #include "common/scope_exit.h" #include "common/string_util.h" @@ -152,6 +153,8 @@ int main(int argc, char** argv) { MicroProfileOnThreadCreate("EmuThread"); SCOPE_EXIT({ MicroProfileShutdown(); }); + Common::ConfigureNvidiaEnvironmentFlags(); + if (filepath.empty()) { LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); return -1; |