summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--externals/CMakeLists.txt3
-rw-r--r--externals/renderdoc/renderdoc_app.h744
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt113
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt46
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt44
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt44
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt64
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt11
-rw-r--r--src/android/app/src/main/jni/native.cpp21
-rw-r--r--src/android/app/src/main/res/drawable/shortcut.xml11
-rw-r--r--src/android/app/src/main/res/navigation/emulation_navigation.xml2
-rw-r--r--src/android/app/src/main/res/navigation/home_navigation.xml2
-rw-r--r--src/android/app/src/main/res/navigation/settings_navigation.xml2
-rw-r--r--src/android/app/src/main/res/values/dimens.xml1
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/fs/path_util.cpp6
-rw-r--r--src/common/logging/filter.cpp2
-rw-r--r--src/common/logging/types.h2
-rw-r--r--src/common/polyfill_thread.h20
-rw-r--r--src/common/settings.h2
-rw-r--r--src/core/CMakeLists.txt25
-rw-r--r--src/core/core.cpp19
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/file_sys/nca_metadata.cpp4
-rw-r--r--src/core/file_sys/nca_metadata.h1
-rw-r--r--src/core/file_sys/registered_cache.cpp28
-rw-r--r--src/core/file_sys/registered_cache.h5
-rw-r--r--src/core/hle/service/am/am.cpp57
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp11
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit_types.h3
-rw-r--r--src/core/hle/service/mii/mii.cpp139
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp717
-rw-r--r--src/core/hle/service/mii/mii_manager.h43
-rw-r--r--src/core/hle/service/mii/mii_result.h20
-rw-r--r--src/core/hle/service/mii/mii_types.h694
-rw-r--r--src/core/hle/service/mii/mii_util.h59
-rw-r--r--src/core/hle/service/mii/raw_data.h27
-rw-r--r--src/core/hle/service/mii/types.h553
-rw-r--r--src/core/hle/service/mii/types/char_info.cpp482
-rw-r--r--src/core/hle/service/mii/types/char_info.h137
-rw-r--r--src/core/hle/service/mii/types/core_data.cpp601
-rw-r--r--src/core/hle/service/mii/types/core_data.h216
-rw-r--r--src/core/hle/service/mii/types/raw_data.cpp (renamed from src/core/hle/service/mii/raw_data.cpp)1395
-rw-r--r--src/core/hle/service/mii/types/raw_data.h73
-rw-r--r--src/core/hle/service/mii/types/store_data.cpp643
-rw-r--r--src/core/hle/service/mii/types/store_data.h145
-rw-r--r--src/core/hle/service/mii/types/ver3_store_data.cpp241
-rw-r--r--src/core/hle/service/mii/types/ver3_store_data.h160
-rw-r--r--src/core/hle/service/nfc/common/device.cpp25
-rw-r--r--src/core/hle/service/nfp/nfp_types.h6
-rw-r--r--src/core/hle/service/ngc/ngc.cpp150
-rw-r--r--src/core/hle/service/ngc/ngc.h (renamed from src/core/hle/service/ngct/ngct.h)4
-rw-r--r--src/core/hle/service/ngct/ngct.cpp62
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/internal_network/network.cpp89
-rw-r--r--src/core/internal_network/network.h3
-rw-r--r--src/core/tools/renderdoc.cpp55
-rw-r--r--src/core/tools/renderdoc.h22
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h1
-rw-r--r--src/video_core/texture_cache/texture_cache.h15
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui99
-rw-r--r--src/yuzu/game_list.cpp6
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/hotkeys.h4
-rw-r--r--src/yuzu/main.cpp124
-rw-r--r--src/yuzu/main.h12
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
93 files changed, 6343 insertions, 2452 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c6b158985..3b0891adf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,6 +63,8 @@ option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
+option(YUZU_ENABLE_PORTABLE "Allow yuzu to enable portable mode if a user folder is found in the CWD" ON)
+
CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
CMAKE_DEPENDENT_OPTION(USE_SYSTEM_MOLTENVK "Use the system MoltenVK lib (instead of the bundled one)" OFF "APPLE" OFF)
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 82a6da9fd..a4c2ffc10 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -174,6 +174,9 @@ target_include_directories(stb PUBLIC ./stb)
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
+add_library(renderdoc INTERFACE)
+target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
+
if (ANDROID)
if (ARCHITECTURE_arm64)
add_subdirectory(libadrenotools)
diff --git a/externals/renderdoc/renderdoc_app.h b/externals/renderdoc/renderdoc_app.h
new file mode 100644
index 000000000..0f4a1f98b
--- /dev/null
+++ b/externals/renderdoc/renderdoc_app.h
@@ -0,0 +1,744 @@
+// SPDX-FileCopyrightText: Baldur Karlsson
+// SPDX-License-Identifier: MIT
+
+/******************************************************************************
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2023 Baldur Karlsson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#pragma once
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html
+//
+
+#if !defined(RENDERDOC_NO_STDINT)
+#include <stdint.h>
+#endif
+
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define RENDERDOC_CC __cdecl
+#elif defined(__linux__)
+#define RENDERDOC_CC
+#elif defined(__APPLE__)
+#define RENDERDOC_CC
+#else
+#error "Unknown platform"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Constants not used directly in below API
+
+// This is a GUID/magic value used for when applications pass a path where shader debug
+// information can be found to match up with a stripped shader.
+// the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue =
+// RENDERDOC_ShaderDebugMagicValue_value
+#define RENDERDOC_ShaderDebugMagicValue_struct \
+ { \
+ 0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
+ }
+
+// as an alternative when you want a byte array (assuming x86 endianness):
+#define RENDERDOC_ShaderDebugMagicValue_bytearray \
+ { \
+ 0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
+ }
+
+// truncated version when only a uint64_t is available (e.g. Vulkan tags):
+#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// RenderDoc capture options
+//
+
+typedef enum RENDERDOC_CaptureOption
+{
+ // Allow the application to enable vsync
+ //
+ // Default - enabled
+ //
+ // 1 - The application can enable or disable vsync at will
+ // 0 - vsync is force disabled
+ eRENDERDOC_Option_AllowVSync = 0,
+
+ // Allow the application to enable fullscreen
+ //
+ // Default - enabled
+ //
+ // 1 - The application can enable or disable fullscreen at will
+ // 0 - fullscreen is force disabled
+ eRENDERDOC_Option_AllowFullscreen = 1,
+
+ // Record API debugging events and messages
+ //
+ // Default - disabled
+ //
+ // 1 - Enable built-in API debugging features and records the results into
+ // the capture, which is matched up with events on replay
+ // 0 - no API debugging is forcibly enabled
+ eRENDERDOC_Option_APIValidation = 2,
+ eRENDERDOC_Option_DebugDeviceMode = 2, // deprecated name of this enum
+
+ // Capture CPU callstacks for API events
+ //
+ // Default - disabled
+ //
+ // 1 - Enables capturing of callstacks
+ // 0 - no callstacks are captured
+ eRENDERDOC_Option_CaptureCallstacks = 3,
+
+ // When capturing CPU callstacks, only capture them from actions.
+ // This option does nothing without the above option being enabled
+ //
+ // Default - disabled
+ //
+ // 1 - Only captures callstacks for actions.
+ // Ignored if CaptureCallstacks is disabled
+ // 0 - Callstacks, if enabled, are captured for every event.
+ eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4,
+ eRENDERDOC_Option_CaptureCallstacksOnlyActions = 4,
+
+ // Specify a delay in seconds to wait for a debugger to attach, after
+ // creating or injecting into a process, before continuing to allow it to run.
+ //
+ // 0 indicates no delay, and the process will run immediately after injection
+ //
+ // Default - 0 seconds
+ //
+ eRENDERDOC_Option_DelayForDebugger = 5,
+
+ // Verify buffer access. This includes checking the memory returned by a Map() call to
+ // detect any out-of-bounds modification, as well as initialising buffers with undefined contents
+ // to a marker value to catch use of uninitialised memory.
+ //
+ // NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do
+ // not do the same kind of interception & checking and undefined contents are really undefined.
+ //
+ // Default - disabled
+ //
+ // 1 - Verify buffer access
+ // 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in
+ // RenderDoc.
+ eRENDERDOC_Option_VerifyBufferAccess = 6,
+
+ // The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites.
+ // This option now controls the filling of uninitialised buffers with 0xdddddddd which was
+ // previously always enabled
+ eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess,
+
+ // Hooks any system API calls that create child processes, and injects
+ // RenderDoc into them recursively with the same options.
+ //
+ // Default - disabled
+ //
+ // 1 - Hooks into spawned child processes
+ // 0 - Child processes are not hooked by RenderDoc
+ eRENDERDOC_Option_HookIntoChildren = 7,
+
+ // By default RenderDoc only includes resources in the final capture necessary
+ // for that frame, this allows you to override that behaviour.
+ //
+ // Default - disabled
+ //
+ // 1 - all live resources at the time of capture are included in the capture
+ // and available for inspection
+ // 0 - only the resources referenced by the captured frame are included
+ eRENDERDOC_Option_RefAllResources = 8,
+
+ // **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or
+ // getting it will be ignored, to allow compatibility with older versions.
+ // In v1.1 the option acts as if it's always enabled.
+ //
+ // By default RenderDoc skips saving initial states for resources where the
+ // previous contents don't appear to be used, assuming that writes before
+ // reads indicate previous contents aren't used.
+ //
+ // Default - disabled
+ //
+ // 1 - initial contents at the start of each captured frame are saved, even if
+ // they are later overwritten or cleared before being used.
+ // 0 - unless a read is detected, initial contents will not be saved and will
+ // appear as black or empty data.
+ eRENDERDOC_Option_SaveAllInitials = 9,
+
+ // In APIs that allow for the recording of command lists to be replayed later,
+ // RenderDoc may choose to not capture command lists before a frame capture is
+ // triggered, to reduce overheads. This means any command lists recorded once
+ // and replayed many times will not be available and may cause a failure to
+ // capture.
+ //
+ // NOTE: This is only true for APIs where multithreading is difficult or
+ // discouraged. Newer APIs like Vulkan and D3D12 will ignore this option
+ // and always capture all command lists since the API is heavily oriented
+ // around it and the overheads have been reduced by API design.
+ //
+ // 1 - All command lists are captured from the start of the application
+ // 0 - Command lists are only captured if their recording begins during
+ // the period when a frame capture is in progress.
+ eRENDERDOC_Option_CaptureAllCmdLists = 10,
+
+ // Mute API debugging output when the API validation mode option is enabled
+ //
+ // Default - enabled
+ //
+ // 1 - Mute any API debug messages from being displayed or passed through
+ // 0 - API debugging is displayed as normal
+ eRENDERDOC_Option_DebugOutputMute = 11,
+
+ // Option to allow vendor extensions to be used even when they may be
+ // incompatible with RenderDoc and cause corrupted replays or crashes.
+ //
+ // Default - inactive
+ //
+ // No values are documented, this option should only be used when absolutely
+ // necessary as directed by a RenderDoc developer.
+ eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12,
+
+ // Define a soft memory limit which some APIs may aim to keep overhead under where
+ // possible. Anything above this limit will where possible be saved directly to disk during
+ // capture.
+ // This will cause increased disk space use (which may cause a capture to fail if disk space is
+ // exhausted) as well as slower capture times.
+ //
+ // Not all memory allocations may be deferred like this so it is not a guarantee of a memory
+ // limit.
+ //
+ // Units are in MBs, suggested values would range from 200MB to 1000MB.
+ //
+ // Default - 0 Megabytes
+ eRENDERDOC_Option_SoftMemoryLimit = 13,
+} RENDERDOC_CaptureOption;
+
+// Sets an option that controls how RenderDoc behaves on capture.
+//
+// Returns 1 if the option and value are valid
+// Returns 0 if either is invalid and the option is unchanged
+typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val);
+typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val);
+
+// Gets the current value of an option as a uint32_t
+//
+// If the option is invalid, 0xffffffff is returned
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt);
+
+// Gets the current value of an option as a float
+//
+// If the option is invalid, -FLT_MAX is returned
+typedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt);
+
+typedef enum RENDERDOC_InputButton
+{
+ // '0' - '9' matches ASCII values
+ eRENDERDOC_Key_0 = 0x30,
+ eRENDERDOC_Key_1 = 0x31,
+ eRENDERDOC_Key_2 = 0x32,
+ eRENDERDOC_Key_3 = 0x33,
+ eRENDERDOC_Key_4 = 0x34,
+ eRENDERDOC_Key_5 = 0x35,
+ eRENDERDOC_Key_6 = 0x36,
+ eRENDERDOC_Key_7 = 0x37,
+ eRENDERDOC_Key_8 = 0x38,
+ eRENDERDOC_Key_9 = 0x39,
+
+ // 'A' - 'Z' matches ASCII values
+ eRENDERDOC_Key_A = 0x41,
+ eRENDERDOC_Key_B = 0x42,
+ eRENDERDOC_Key_C = 0x43,
+ eRENDERDOC_Key_D = 0x44,
+ eRENDERDOC_Key_E = 0x45,
+ eRENDERDOC_Key_F = 0x46,
+ eRENDERDOC_Key_G = 0x47,
+ eRENDERDOC_Key_H = 0x48,
+ eRENDERDOC_Key_I = 0x49,
+ eRENDERDOC_Key_J = 0x4A,
+ eRENDERDOC_Key_K = 0x4B,
+ eRENDERDOC_Key_L = 0x4C,
+ eRENDERDOC_Key_M = 0x4D,
+ eRENDERDOC_Key_N = 0x4E,
+ eRENDERDOC_Key_O = 0x4F,
+ eRENDERDOC_Key_P = 0x50,
+ eRENDERDOC_Key_Q = 0x51,
+ eRENDERDOC_Key_R = 0x52,
+ eRENDERDOC_Key_S = 0x53,
+ eRENDERDOC_Key_T = 0x54,
+ eRENDERDOC_Key_U = 0x55,
+ eRENDERDOC_Key_V = 0x56,
+ eRENDERDOC_Key_W = 0x57,
+ eRENDERDOC_Key_X = 0x58,
+ eRENDERDOC_Key_Y = 0x59,
+ eRENDERDOC_Key_Z = 0x5A,
+
+ // leave the rest of the ASCII range free
+ // in case we want to use it later
+ eRENDERDOC_Key_NonPrintable = 0x100,
+
+ eRENDERDOC_Key_Divide,
+ eRENDERDOC_Key_Multiply,
+ eRENDERDOC_Key_Subtract,
+ eRENDERDOC_Key_Plus,
+
+ eRENDERDOC_Key_F1,
+ eRENDERDOC_Key_F2,
+ eRENDERDOC_Key_F3,
+ eRENDERDOC_Key_F4,
+ eRENDERDOC_Key_F5,
+ eRENDERDOC_Key_F6,
+ eRENDERDOC_Key_F7,
+ eRENDERDOC_Key_F8,
+ eRENDERDOC_Key_F9,
+ eRENDERDOC_Key_F10,
+ eRENDERDOC_Key_F11,
+ eRENDERDOC_Key_F12,
+
+ eRENDERDOC_Key_Home,
+ eRENDERDOC_Key_End,
+ eRENDERDOC_Key_Insert,
+ eRENDERDOC_Key_Delete,
+ eRENDERDOC_Key_PageUp,
+ eRENDERDOC_Key_PageDn,
+
+ eRENDERDOC_Key_Backspace,
+ eRENDERDOC_Key_Tab,
+ eRENDERDOC_Key_PrtScrn,
+ eRENDERDOC_Key_Pause,
+
+ eRENDERDOC_Key_Max,
+} RENDERDOC_InputButton;
+
+// Sets which key or keys can be used to toggle focus between multiple windows
+//
+// If keys is NULL or num is 0, toggle keys will be disabled
+typedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num);
+
+// Sets which key or keys can be used to capture the next frame
+//
+// If keys is NULL or num is 0, captures keys will be disabled
+typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num);
+
+typedef enum RENDERDOC_OverlayBits
+{
+ // This single bit controls whether the overlay is enabled or disabled globally
+ eRENDERDOC_Overlay_Enabled = 0x1,
+
+ // Show the average framerate over several seconds as well as min/max
+ eRENDERDOC_Overlay_FrameRate = 0x2,
+
+ // Show the current frame number
+ eRENDERDOC_Overlay_FrameNumber = 0x4,
+
+ // Show a list of recent captures, and how many captures have been made
+ eRENDERDOC_Overlay_CaptureList = 0x8,
+
+ // Default values for the overlay mask
+ eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate |
+ eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList),
+
+ // Enable all bits
+ eRENDERDOC_Overlay_All = ~0U,
+
+ // Disable all bits
+ eRENDERDOC_Overlay_None = 0,
+} RENDERDOC_OverlayBits;
+
+// returns the overlay bits that have been set
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)();
+// sets the overlay bits with an and & or mask
+typedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or);
+
+// this function will attempt to remove RenderDoc's hooks in the application.
+//
+// Note: that this can only work correctly if done immediately after
+// the module is loaded, before any API work happens. RenderDoc will remove its
+// injected hooks and shut down. Behaviour is undefined if this is called
+// after any API functions have been called, and there is still no guarantee of
+// success.
+typedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)();
+
+// DEPRECATED: compatibility for code compiled against pre-1.4.1 headers.
+typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown;
+
+// This function will unload RenderDoc's crash handler.
+//
+// If you use your own crash handler and don't want RenderDoc's handler to
+// intercede, you can call this function to unload it and any unhandled
+// exceptions will pass to the next handler.
+typedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)();
+
+// Sets the capture file path template
+//
+// pathtemplate is a UTF-8 string that gives a template for how captures will be named
+// and where they will be saved.
+//
+// Any extension is stripped off the path, and captures are saved in the directory
+// specified, and named with the filename and the frame number appended. If the
+// directory does not exist it will be created, including any parent directories.
+//
+// If pathtemplate is NULL, the template will remain unchanged
+//
+// Example:
+//
+// SetCaptureFilePathTemplate("my_captures/example");
+//
+// Capture #1 -> my_captures/example_frame123.rdc
+// Capture #2 -> my_captures/example_frame456.rdc
+typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate);
+
+// returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string
+typedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)();
+
+// DEPRECATED: compatibility for code compiled against pre-1.1.2 headers.
+typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate;
+typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate;
+
+// returns the number of captures that have been made
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)();
+
+// This function returns the details of a capture, by index. New captures are added
+// to the end of the list.
+//
+// filename will be filled with the absolute path to the capture file, as a UTF-8 string
+// pathlength will be written with the length in bytes of the filename string
+// timestamp will be written with the time of the capture, in seconds since the Unix epoch
+//
+// Any of the parameters can be NULL and they'll be skipped.
+//
+// The function will return 1 if the capture index is valid, or 0 if the index is invalid
+// If the index is invalid, the values will be unchanged
+//
+// Note: when captures are deleted in the UI they will remain in this list, so the
+// capture path may not exist anymore.
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename,
+ uint32_t *pathlength, uint64_t *timestamp);
+
+// Sets the comments associated with a capture file. These comments are displayed in the
+// UI program when opening.
+//
+// filePath should be a path to the capture file to add comments to. If set to NULL or ""
+// the most recent capture file created made will be used instead.
+// comments should be a NULL-terminated UTF-8 string to add as comments.
+//
+// Any existing comments will be overwritten.
+typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath,
+ const char *comments);
+
+// returns 1 if the RenderDoc UI is connected to this application, 0 otherwise
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)();
+
+// DEPRECATED: compatibility for code compiled against pre-1.1.1 headers.
+// This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for
+// backwards compatibility with old code, it is castable either way since it's ABI compatible
+// as the same function pointer type.
+typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected;
+
+// This function will launch the Replay UI associated with the RenderDoc library injected
+// into the running application.
+//
+// if connectTargetControl is 1, the Replay UI will be launched with a command line parameter
+// to connect to this application
+// cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open
+// if cmdline is NULL, the command line will be empty.
+//
+// returns the PID of the replay UI if successful, 0 if not successful.
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl,
+ const char *cmdline);
+
+// RenderDoc can return a higher version than requested if it's backwards compatible,
+// this function returns the actual version returned. If a parameter is NULL, it will be
+// ignored and the others will be filled out.
+typedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch);
+
+// Requests that the replay UI show itself (if hidden or not the current top window). This can be
+// used in conjunction with IsTargetControlConnected and LaunchReplayUI to intelligently handle
+// showing the UI after making a capture.
+//
+// This will return 1 if the request was successfully passed on, though it's not guaranteed that
+// the UI will be on top in all cases depending on OS rules. It will return 0 if there is no current
+// target control connection to make such a request, or if there was another error
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_ShowReplayUI)();
+
+//////////////////////////////////////////////////////////////////////////
+// Capturing functions
+//
+
+// A device pointer is a pointer to the API's root handle.
+//
+// This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc
+typedef void *RENDERDOC_DevicePointer;
+
+// A window handle is the OS's native window handle
+//
+// This would be an HWND, GLXDrawable, etc
+typedef void *RENDERDOC_WindowHandle;
+
+// A helper macro for Vulkan, where the device handle cannot be used directly.
+//
+// Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use.
+//
+// Specifically, the value needed is the dispatch table pointer, which sits as the first
+// pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and
+// indirect once.
+#define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst)))
+
+// This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will
+// respond to keypresses. Neither parameter can be NULL
+typedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device,
+ RENDERDOC_WindowHandle wndHandle);
+
+// capture the next frame on whichever window and API is currently considered active
+typedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)();
+
+// capture the next N frames on whichever window and API is currently considered active
+typedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames);
+
+// When choosing either a device pointer or a window handle to capture, you can pass NULL.
+// Passing NULL specifies a 'wildcard' match against anything. This allows you to specify
+// any API rendering to a specific window, or a specific API instance rendering to any window,
+// or in the simplest case of one window and one API, you can just pass NULL for both.
+//
+// In either case, if there are two or more possible matching (device,window) pairs it
+// is undefined which one will be captured.
+//
+// Note: for headless rendering you can pass NULL for the window handle and either specify
+// a device pointer or leave it NULL as above.
+
+// Immediately starts capturing API calls on the specified device pointer and window handle.
+//
+// If there is no matching thing to capture (e.g. no supported API has been initialised),
+// this will do nothing.
+//
+// The results are undefined (including crashes) if two captures are started overlapping,
+// even on separate devices and/oror windows.
+typedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device,
+ RENDERDOC_WindowHandle wndHandle);
+
+// Returns whether or not a frame capture is currently ongoing anywhere.
+//
+// This will return 1 if a capture is ongoing, and 0 if there is no capture running
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)();
+
+// Ends capturing immediately.
+//
+// This will return 1 if the capture succeeded, and 0 if there was an error capturing.
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device,
+ RENDERDOC_WindowHandle wndHandle);
+
+// Ends capturing immediately and discard any data stored without saving to disk.
+//
+// This will return 1 if the capture was discarded, and 0 if there was an error or no capture
+// was in progress
+typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device,
+ RENDERDOC_WindowHandle wndHandle);
+
+// Only valid to be called between a call to StartFrameCapture and EndFrameCapture. Gives a custom
+// title to the capture produced which will be displayed in the UI.
+//
+// If multiple captures are ongoing, this title will be applied to the first capture to end after
+// this call. The second capture to end will have no title, unless this function is called again.
+//
+// Calling this function has no effect if no capture is currently running, and if it is called
+// multiple times only the last title will be used.
+typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureTitle)(const char *title);
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// RenderDoc API versions
+//
+
+// RenderDoc uses semantic versioning (http://semver.org/).
+//
+// MAJOR version is incremented when incompatible API changes happen.
+// MINOR version is incremented when functionality is added in a backwards-compatible manner.
+// PATCH version is incremented when backwards-compatible bug fixes happen.
+//
+// Note that this means the API returned can be higher than the one you might have requested.
+// e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned
+// instead of 1.0.0. You can check this with the GetAPIVersion entry point
+typedef enum RENDERDOC_Version
+{
+ eRENDERDOC_API_Version_1_0_0 = 10000, // RENDERDOC_API_1_0_0 = 1 00 00
+ eRENDERDOC_API_Version_1_0_1 = 10001, // RENDERDOC_API_1_0_1 = 1 00 01
+ eRENDERDOC_API_Version_1_0_2 = 10002, // RENDERDOC_API_1_0_2 = 1 00 02
+ eRENDERDOC_API_Version_1_1_0 = 10100, // RENDERDOC_API_1_1_0 = 1 01 00
+ eRENDERDOC_API_Version_1_1_1 = 10101, // RENDERDOC_API_1_1_1 = 1 01 01
+ eRENDERDOC_API_Version_1_1_2 = 10102, // RENDERDOC_API_1_1_2 = 1 01 02
+ eRENDERDOC_API_Version_1_2_0 = 10200, // RENDERDOC_API_1_2_0 = 1 02 00
+ eRENDERDOC_API_Version_1_3_0 = 10300, // RENDERDOC_API_1_3_0 = 1 03 00
+ eRENDERDOC_API_Version_1_4_0 = 10400, // RENDERDOC_API_1_4_0 = 1 04 00
+ eRENDERDOC_API_Version_1_4_1 = 10401, // RENDERDOC_API_1_4_1 = 1 04 01
+ eRENDERDOC_API_Version_1_4_2 = 10402, // RENDERDOC_API_1_4_2 = 1 04 02
+ eRENDERDOC_API_Version_1_5_0 = 10500, // RENDERDOC_API_1_5_0 = 1 05 00
+ eRENDERDOC_API_Version_1_6_0 = 10600, // RENDERDOC_API_1_6_0 = 1 06 00
+} RENDERDOC_Version;
+
+// API version changelog:
+//
+// 1.0.0 - initial release
+// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered
+// by keypress or TriggerCapture, instead of Start/EndFrameCapture.
+// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation
+// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new
+// function pointer is added to the end of the struct, the original layout is identical
+// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote
+// replay/remote server concept in replay UI)
+// 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these
+// are captures and not debug logging files. This is the first API version in the v1.0
+// branch.
+// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be
+// displayed in the UI program on load.
+// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions
+// which allows users to opt-in to allowing unsupported vendor extensions to function.
+// Should be used at the user's own risk.
+// Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to
+// eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to
+// 0xdddddddd of uninitialised buffer contents.
+// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop
+// capturing without saving anything to disk.
+// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening
+// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option.
+// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected
+// 1.6.0 - Added feature: SetCaptureTitle() which can be used to set a title for a
+// capture made with StartFrameCapture() or EndFrameCapture()
+
+typedef struct RENDERDOC_API_1_6_0
+{
+ pRENDERDOC_GetAPIVersion GetAPIVersion;
+
+ pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32;
+ pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32;
+
+ pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32;
+ pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32;
+
+ pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys;
+ pRENDERDOC_SetCaptureKeys SetCaptureKeys;
+
+ pRENDERDOC_GetOverlayBits GetOverlayBits;
+ pRENDERDOC_MaskOverlayBits MaskOverlayBits;
+
+ // Shutdown was renamed to RemoveHooks in 1.4.1.
+ // These unions allow old code to continue compiling without changes
+ union
+ {
+ pRENDERDOC_Shutdown Shutdown;
+ pRENDERDOC_RemoveHooks RemoveHooks;
+ };
+ pRENDERDOC_UnloadCrashHandler UnloadCrashHandler;
+
+ // Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2.
+ // These unions allow old code to continue compiling without changes
+ union
+ {
+ // deprecated name
+ pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate;
+ // current name
+ pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate;
+ };
+ union
+ {
+ // deprecated name
+ pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate;
+ // current name
+ pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate;
+ };
+
+ pRENDERDOC_GetNumCaptures GetNumCaptures;
+ pRENDERDOC_GetCapture GetCapture;
+
+ pRENDERDOC_TriggerCapture TriggerCapture;
+
+ // IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1.
+ // This union allows old code to continue compiling without changes
+ union
+ {
+ // deprecated name
+ pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected;
+ // current name
+ pRENDERDOC_IsTargetControlConnected IsTargetControlConnected;
+ };
+ pRENDERDOC_LaunchReplayUI LaunchReplayUI;
+
+ pRENDERDOC_SetActiveWindow SetActiveWindow;
+
+ pRENDERDOC_StartFrameCapture StartFrameCapture;
+ pRENDERDOC_IsFrameCapturing IsFrameCapturing;
+ pRENDERDOC_EndFrameCapture EndFrameCapture;
+
+ // new function in 1.1.0
+ pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture;
+
+ // new function in 1.2.0
+ pRENDERDOC_SetCaptureFileComments SetCaptureFileComments;
+
+ // new function in 1.4.0
+ pRENDERDOC_DiscardFrameCapture DiscardFrameCapture;
+
+ // new function in 1.5.0
+ pRENDERDOC_ShowReplayUI ShowReplayUI;
+
+ // new function in 1.6.0
+ pRENDERDOC_SetCaptureTitle SetCaptureTitle;
+} RENDERDOC_API_1_6_0;
+
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_0;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_1;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_2;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_0;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_1;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_2;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_2_0;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_3_0;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_0;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_1;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_2;
+typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_5_0;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// RenderDoc API entry point
+//
+// This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available.
+//
+// The name is the same as the typedef - "RENDERDOC_GetAPI"
+//
+// This function is not thread safe, and should not be called on multiple threads at once.
+// Ideally, call this once as early as possible in your application's startup, before doing
+// any API work, since some configuration functionality etc has to be done also before
+// initialising any APIs.
+//
+// Parameters:
+// version is a single value from the RENDERDOC_Version above.
+//
+// outAPIPointers will be filled out with a pointer to the corresponding struct of function
+// pointers.
+//
+// Returns:
+// 1 - if the outAPIPointers has been filled with a pointer to the API struct requested
+// 0 - if the requested version is not supported or the arguments are invalid.
+//
+typedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index c8706d7a6..21f67f32a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -308,21 +308,6 @@ object NativeLibrary {
external fun isPaused(): Boolean
/**
- * Mutes emulation sound
- */
- external fun muteAudio(): Boolean
-
- /**
- * Unmutes emulation sound
- */
- external fun unmuteAudio(): Boolean
-
- /**
- * Returns true if emulation audio is muted.
- */
- external fun isMuted(): Boolean
-
- /**
* Returns the performance stats for the current game
*/
external fun getPerfStats(): DoubleArray
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index bbd328c71..d4ae39661 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -332,7 +332,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
pictureInPictureActions.add(pauseRemoteAction)
}
- if (NativeLibrary.isMuted()) {
+ if (BooleanSetting.AUDIO_MUTED.boolean) {
val unmuteIcon = Icon.createWithResource(
this@EmulationActivity,
R.drawable.ic_pip_unmute
@@ -389,9 +389,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
}
if (intent.action == actionUnmute) {
- if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
+ if (BooleanSetting.AUDIO_MUTED.boolean) BooleanSetting.AUDIO_MUTED.setBoolean(false)
} else if (intent.action == actionMute) {
- if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio()
+ if (!BooleanSetting.AUDIO_MUTED.boolean) BooleanSetting.AUDIO_MUTED.setBoolean(true)
}
buildPictureInPictureParams()
}
@@ -417,7 +417,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
} catch (ignored: Exception) {
}
// Always resume audio, since there is no UI button
- if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
+ if (BooleanSetting.AUDIO_MUTED.boolean) BooleanSetting.AUDIO_MUTED.setBoolean(false)
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 0013e8512..f9f88a1d2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -4,7 +4,8 @@
package org.yuzu.yuzu_emu.adapters
import android.content.Intent
-import android.graphics.drawable.BitmapDrawable
+import android.graphics.Bitmap
+import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
@@ -15,7 +16,10 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
+import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat
+import androidx.core.graphics.drawable.toBitmap
+import androidx.core.graphics.drawable.toDrawable
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.findNavController
@@ -87,11 +91,24 @@ class GameAdapter(private val activity: AppCompatActivity) :
action = Intent.ACTION_VIEW
data = Uri.parse(holder.game.path)
}
+
+ val layerDrawable = ResourcesCompat.getDrawable(
+ YuzuApplication.appContext.resources,
+ R.drawable.shortcut,
+ null
+ ) as LayerDrawable
+ layerDrawable.setDrawableByLayerId(
+ R.id.shortcut_foreground,
+ GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources)
+ )
+ val inset = YuzuApplication.appContext.resources
+ .getDimensionPixelSize(R.dimen.icon_inset)
+ layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title)
.setIcon(
- IconCompat.createWithBitmap(
- (holder.binding.imageGameScreen.drawable as BitmapDrawable).bitmap
+ IconCompat.createWithAdaptiveBitmap(
+ layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index 8d87d3bd7..1675627a1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -10,8 +10,12 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
+import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
+import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -86,7 +90,11 @@ class HomeSettingAdapter(
binding.optionIcon.alpha = 0.5f
}
- option.details.observe(viewLifecycle) { updateOptionDetails(it) }
+ viewLifecycle.lifecycleScope.launch {
+ viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
+ option.details.collect { updateOptionDetails(it) }
+ }
+ }
binding.optionDetail.postDelayed(
{
binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index e0c0538c7..8476ce867 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -10,6 +10,7 @@ enum class BooleanSetting(
override val category: Settings.Category,
override val androidDefault: Boolean? = null
) : AbstractBooleanSetting {
+ AUDIO_MUTED("audio_muted", Settings.Category.Audio),
CPU_DEBUG_MODE("cpu_debug_mode", Settings.Category.Cpu),
FASTMEM("cpuopt_fastmem", Settings.Category.Cpu),
FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.Category.Cpu),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 0702236e8..08e2a973d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -80,6 +80,17 @@ object Settings {
const val SECTION_THEME = "Theme"
const val SECTION_DEBUG = "Debug"
+ enum class MenuTag(val titleId: Int) {
+ SECTION_ROOT(R.string.advanced_settings),
+ SECTION_GENERAL(R.string.preferences_general),
+ SECTION_SYSTEM(R.string.preferences_system),
+ SECTION_RENDERER(R.string.preferences_graphics),
+ SECTION_AUDIO(R.string.preferences_audio),
+ SECTION_CPU(R.string.cpu),
+ SECTION_THEME(R.string.preferences_theme),
+ SECTION_DEBUG(R.string.preferences_debug);
+ }
+
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
const val PREF_OVERLAY_VERSION = "OverlayVersion"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index 91c273964..b343e527e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -3,10 +3,12 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import org.yuzu.yuzu_emu.features.settings.model.Settings
+
class SubmenuSetting(
titleId: Int,
descriptionId: Int,
- val menuKey: String
+ val menuKey: Settings.MenuTag
) : SettingsItem(emptySetting, titleId, descriptionId) {
override val type = TYPE_SUBMENU
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index 908c01265..4d2f2f604 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -13,9 +13,14 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
import java.io.IOException
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@@ -66,25 +71,39 @@ class SettingsActivity : AppCompatActivity() {
)
}
- settingsViewModel.shouldRecreate.observe(this) {
- if (it) {
- settingsViewModel.setShouldRecreate(false)
- recreate()
+ lifecycleScope.apply {
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ settingsViewModel.shouldRecreate.collectLatest {
+ if (it) {
+ settingsViewModel.setShouldRecreate(false)
+ recreate()
+ }
+ }
+ }
}
- }
- settingsViewModel.shouldNavigateBack.observe(this) {
- if (it) {
- settingsViewModel.setShouldNavigateBack(false)
- navigateBack()
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ settingsViewModel.shouldNavigateBack.collectLatest {
+ if (it) {
+ settingsViewModel.setShouldNavigateBack(false)
+ navigateBack()
+ }
+ }
+ }
}
- }
- settingsViewModel.shouldShowResetSettingsDialog.observe(this) {
- if (it) {
- settingsViewModel.setShouldShowResetSettingsDialog(false)
- ResetSettingsDialogFragment().show(
- supportFragmentManager,
- ResetSettingsDialogFragment.TAG
- )
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
+ if (it) {
+ settingsViewModel.setShouldShowResetSettingsDialog(false)
+ ResetSettingsDialogFragment().show(
+ supportFragmentManager,
+ ResetSettingsDialogFragment.TAG
+ )
+ }
+ }
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index bc319714c..70d8ec14b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.features.settings.ui
+import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -13,14 +14,19 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.transition.MaterialSharedAxis
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
-import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
+import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.SettingsViewModel
class SettingsFragment : Fragment() {
@@ -51,15 +57,17 @@ class SettingsFragment : Fragment() {
return binding.root
}
+ // This is using the correct scope, lint is just acting up
+ @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
settingsAdapter = SettingsAdapter(this, requireContext())
presenter = SettingsFragmentPresenter(
settingsViewModel,
settingsAdapter!!,
- args.menuTag,
- args.game?.gameId ?: ""
+ args.menuTag
)
+ binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
val dividerDecoration = MaterialDividerItemDecoration(
requireContext(),
LinearLayoutManager.VERTICAL
@@ -75,28 +83,31 @@ class SettingsFragment : Fragment() {
settingsViewModel.setShouldNavigateBack(true)
}
- settingsViewModel.toolbarTitle.observe(viewLifecycleOwner) {
- if (it.isNotEmpty()) binding.toolbarSettingsLayout.title = it
- }
-
- settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) {
- if (it) {
- settingsViewModel.setShouldReloadSettingsList(false)
- presenter.loadSettingsList()
+ viewLifecycleOwner.lifecycleScope.apply {
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ settingsViewModel.shouldReloadSettingsList.collectLatest {
+ if (it) {
+ settingsViewModel.setShouldReloadSettingsList(false)
+ presenter.loadSettingsList()
+ }
+ }
+ }
}
- }
-
- settingsViewModel.isUsingSearch.observe(viewLifecycleOwner) {
- if (it) {
- reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
- exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
- } else {
- reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
- exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
+ launch {
+ settingsViewModel.isUsingSearch.collectLatest {
+ if (it) {
+ reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
+ exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
+ } else {
+ reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
+ exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
+ }
+ }
}
}
- if (args.menuTag == SettingsFile.FILE_NAME_CONFIG) {
+ if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
binding.toolbarSettings.inflateMenu(R.menu.menu_settings)
binding.toolbarSettings.setOnMenuItemClickListener {
when (it.itemId) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 22a529b1b..766414a6c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
-import android.text.TextUtils
import android.widget.Toast
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.R
@@ -20,15 +19,13 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
-import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.NativeConfig
class SettingsFragmentPresenter(
private val settingsViewModel: SettingsViewModel,
private val adapter: SettingsAdapter,
- private var menuTag: String,
- private var gameId: String
+ private var menuTag: Settings.MenuTag
) {
private var settingsList = ArrayList<SettingsItem>()
@@ -53,24 +50,15 @@ class SettingsFragmentPresenter(
}
fun loadSettingsList() {
- if (!TextUtils.isEmpty(gameId)) {
- settingsViewModel.setToolbarTitle(
- context.getString(
- R.string.advanced_settings_game,
- gameId
- )
- )
- }
-
val sl = ArrayList<SettingsItem>()
when (menuTag) {
- SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl)
- Settings.SECTION_GENERAL -> addGeneralSettings(sl)
- Settings.SECTION_SYSTEM -> addSystemSettings(sl)
- Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
- Settings.SECTION_AUDIO -> addAudioSettings(sl)
- Settings.SECTION_THEME -> addThemeSettings(sl)
- Settings.SECTION_DEBUG -> addDebugSettings(sl)
+ Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
+ Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
+ Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
+ Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
+ Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
+ Settings.MenuTag.SECTION_THEME -> addThemeSettings(sl)
+ Settings.MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
else -> {
val context = YuzuApplication.appContext
Toast.makeText(
@@ -86,13 +74,12 @@ class SettingsFragmentPresenter(
}
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
- settingsViewModel.setToolbarTitle(context.getString(R.string.advanced_settings))
sl.apply {
- add(SubmenuSetting(R.string.preferences_general, 0, Settings.SECTION_GENERAL))
- add(SubmenuSetting(R.string.preferences_system, 0, Settings.SECTION_SYSTEM))
- add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.SECTION_RENDERER))
- add(SubmenuSetting(R.string.preferences_audio, 0, Settings.SECTION_AUDIO))
- add(SubmenuSetting(R.string.preferences_debug, 0, Settings.SECTION_DEBUG))
+ add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
+ add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
+ add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
+ add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
+ add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
add(
RunnableSetting(R.string.reset_to_default, 0, false) {
settingsViewModel.setShouldShowResetSettingsDialog(true)
@@ -102,7 +89,6 @@ class SettingsFragmentPresenter(
}
private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
- settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_general))
sl.apply {
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
@@ -112,7 +98,6 @@ class SettingsFragmentPresenter(
}
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
- settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_system))
sl.apply {
add(BooleanSetting.USE_DOCKED_MODE.key)
add(IntSetting.REGION_INDEX.key)
@@ -123,7 +108,6 @@ class SettingsFragmentPresenter(
}
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
- settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_graphics))
sl.apply {
add(IntSetting.RENDERER_ACCURACY.key)
add(IntSetting.RENDERER_RESOLUTION.key)
@@ -140,7 +124,6 @@ class SettingsFragmentPresenter(
}
private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
- settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_audio))
sl.apply {
add(IntSetting.AUDIO_OUTPUT_ENGINE.key)
add(ByteSetting.AUDIO_VOLUME.key)
@@ -148,7 +131,6 @@ class SettingsFragmentPresenter(
}
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
- settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_theme))
sl.apply {
val theme: AbstractIntSetting = object : AbstractIntSetting {
override val int: Int
@@ -261,7 +243,6 @@ class SettingsFragmentPresenter(
}
private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
- settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_debug))
sl.apply {
add(HeaderSetting(R.string.gpu))
add(IntSetting.RENDERER_BACKEND.key)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 944ae652e..3e6c157c7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -39,6 +39,7 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
@@ -49,7 +50,6 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay
@@ -129,6 +129,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return binding.root
}
+ // This is using the correct scope, lint is just acting up
+ @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.surfaceEmulation.holder.addCallback(this)
binding.showFpsText.setTextColor(Color.YELLOW)
@@ -163,7 +165,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_settings -> {
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
- SettingsFile.FILE_NAME_CONFIG
+ Settings.MenuTag.SECTION_ROOT
)
binding.root.findNavController().navigate(action)
true
@@ -205,59 +207,80 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
)
- viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
- lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
- WindowInfoTracker.getOrCreate(requireContext())
- .windowLayoutInfo(requireActivity())
- .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
- }
- }
-
GameIconUtils.loadGameIcon(game, binding.loadingImage)
binding.loadingTitle.text = game.title
binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true
- emulationViewModel.shaderProgress.observe(viewLifecycleOwner) {
- if (it > 0 && it != emulationViewModel.totalShaders.value!!) {
- binding.loadingProgressIndicator.isIndeterminate = false
-
- if (it < binding.loadingProgressIndicator.max) {
- binding.loadingProgressIndicator.progress = it
+ viewLifecycleOwner.lifecycleScope.apply {
+ launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ WindowInfoTracker.getOrCreate(requireContext())
+ .windowLayoutInfo(requireActivity())
+ .collect {
+ updateFoldableLayout(requireActivity() as EmulationActivity, it)
+ }
}
}
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.shaderProgress.collectLatest {
+ if (it > 0 && it != emulationViewModel.totalShaders.value) {
+ binding.loadingProgressIndicator.isIndeterminate = false
+
+ if (it < binding.loadingProgressIndicator.max) {
+ binding.loadingProgressIndicator.progress = it
+ }
+ }
- if (it == emulationViewModel.totalShaders.value!!) {
- binding.loadingText.setText(R.string.loading)
- binding.loadingProgressIndicator.isIndeterminate = true
+ if (it == emulationViewModel.totalShaders.value) {
+ binding.loadingText.setText(R.string.loading)
+ binding.loadingProgressIndicator.isIndeterminate = true
+ }
+ }
+ }
}
- }
- emulationViewModel.totalShaders.observe(viewLifecycleOwner) {
- binding.loadingProgressIndicator.max = it
- }
- emulationViewModel.shaderMessage.observe(viewLifecycleOwner) {
- if (it.isNotEmpty()) {
- binding.loadingText.text = it
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.totalShaders.collectLatest {
+ binding.loadingProgressIndicator.max = it
+ }
+ }
}
- }
-
- emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started ->
- if (started) {
- binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
- ViewUtils.showView(binding.surfaceInputOverlay)
- ViewUtils.hideView(binding.loadingIndicator)
-
- // Setup overlay
- updateShowFpsOverlay()
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.shaderMessage.collectLatest {
+ if (it.isNotEmpty()) {
+ binding.loadingText.text = it
+ }
+ }
+ }
}
- }
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.emulationStarted.collectLatest {
+ if (it) {
+ binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
+ ViewUtils.showView(binding.surfaceInputOverlay)
+ ViewUtils.hideView(binding.loadingIndicator)
- emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) {
- if (it) {
- binding.loadingText.setText(R.string.shutting_down)
- ViewUtils.showView(binding.loadingIndicator)
- ViewUtils.hideView(binding.inputContainer)
- ViewUtils.hideView(binding.showFpsText)
+ // Setup overlay
+ updateShowFpsOverlay()
+ }
+ }
+ }
+ }
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.isEmulationStopping.collectLatest {
+ if (it) {
+ binding.loadingText.setText(R.string.shutting_down)
+ ViewUtils.showView(binding.loadingIndicator)
+ ViewUtils.hideView(binding.inputContainer)
+ ViewUtils.hideView(binding.showFpsText)
+ }
+ }
+ }
}
}
}
@@ -274,9 +297,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
} else {
- if (EmulationMenuSettings.showOverlay &&
- emulationViewModel.emulationStarted.value == true
- ) {
+ if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) {
binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.visibility = View.VISIBLE
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index cbbe14d22..c119e69c9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -37,7 +37,6 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
@@ -78,7 +77,7 @@ class HomeSettingsFragment : Fragment() {
{
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
- SettingsFile.FILE_NAME_CONFIG
+ Settings.MenuTag.SECTION_ROOT
)
binding.root.findNavController().navigate(action)
}
@@ -100,7 +99,7 @@ class HomeSettingsFragment : Fragment() {
{
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
- Settings.SECTION_THEME
+ Settings.MenuTag.SECTION_THEME
)
binding.root.findNavController().navigate(action)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
index 181bd983a..ea8eb073a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
@@ -9,8 +9,12 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel
@@ -28,21 +32,27 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
.create()
dialog.setCanceledOnTouchOutside(false)
- taskViewModel.isComplete.observe(this) { complete ->
- if (complete) {
- dialog.dismiss()
- when (val result = taskViewModel.result.value) {
- is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show()
- is MessageDialogFragment -> result.show(
- requireActivity().supportFragmentManager,
- MessageDialogFragment.TAG
- )
+ viewLifecycleOwner.lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ taskViewModel.isComplete.collect {
+ if (it) {
+ dialog.dismiss()
+ when (val result = taskViewModel.result.value) {
+ is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG)
+ .show()
+
+ is MessageDialogFragment -> result.show(
+ requireActivity().supportFragmentManager,
+ MessageDialogFragment.TAG
+ )
+ }
+ taskViewModel.clear()
+ }
}
- taskViewModel.clear()
}
}
- if (taskViewModel.isRunning.value == false) {
+ if (!taskViewModel.isRunning.value) {
taskViewModel.runTask()
}
return dialog
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index f54dccc69..2dbca76a5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.fragments
+import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
@@ -17,9 +18,13 @@ import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.PreferenceManager
import info.debatty.java.stringsimilarity.Jaccard
import info.debatty.java.stringsimilarity.JaroWinkler
+import kotlinx.coroutines.launch
import java.util.Locale
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@@ -52,6 +57,8 @@ class SearchFragment : Fragment() {
return binding.root
}
+ // This is using the correct scope, lint is just acting up
+ @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
homeViewModel.setNavigationVisibility(visible = true, animated = false)
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
@@ -79,21 +86,32 @@ class SearchFragment : Fragment() {
filterAndSearch()
}
- gamesViewModel.apply {
- searchFocused.observe(viewLifecycleOwner) { searchFocused ->
- if (searchFocused) {
- focusSearch()
- gamesViewModel.setSearchFocused(false)
+ viewLifecycleOwner.lifecycleScope.apply {
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ gamesViewModel.searchFocused.collect {
+ if (it) {
+ focusSearch()
+ gamesViewModel.setSearchFocused(false)
+ }
+ }
}
}
-
- games.observe(viewLifecycleOwner) { filterAndSearch() }
- searchedGames.observe(viewLifecycleOwner) {
- (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
- if (it.isEmpty()) {
- binding.noResultsView.visibility = View.VISIBLE
- } else {
- binding.noResultsView.visibility = View.GONE
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ gamesViewModel.games.collect { filterAndSearch() }
+ }
+ }
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ gamesViewModel.searchedGames.collect {
+ (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
+ if (it.isEmpty()) {
+ binding.noResultsView.visibility = View.VISIBLE
+ } else {
+ binding.noResultsView.visibility = View.GONE
+ }
+ }
}
}
}
@@ -109,7 +127,7 @@ class SearchFragment : Fragment() {
private inner class ScoredGame(val score: Double, val item: Game)
private fun filterAndSearch() {
- val baseList = gamesViewModel.games.value!!
+ val baseList = gamesViewModel.games.value
val filteredList: List<Game> = when (binding.chipGroup.checkedChipId) {
R.id.chip_recently_played -> {
baseList.filter {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
index 55b6a0367..9d0594c6e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
@@ -15,10 +15,14 @@ import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.transition.MaterialSharedAxis
import info.debatty.java.stringsimilarity.Cosine
+import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
@@ -79,10 +83,14 @@ class SettingsSearchFragment : Fragment() {
search()
binding.settingsList.smoothScrollToPosition(0)
}
- settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) {
- if (it) {
- settingsViewModel.setShouldReloadSettingsList(false)
- search()
+ viewLifecycleOwner.lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ settingsViewModel.shouldReloadSettingsList.collect {
+ if (it) {
+ settingsViewModel.setShouldReloadSettingsList(false)
+ search()
+ }
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index d50c421a0..fbb2f6e18 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -22,10 +22,14 @@ import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.transition.MaterialFadeThrough
+import kotlinx.coroutines.launch
import java.io.File
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@@ -206,10 +210,14 @@ class SetupFragment : Fragment() {
)
}
- homeViewModel.shouldPageForward.observe(viewLifecycleOwner) {
- if (it) {
- pageForward()
- homeViewModel.setShouldPageForward(false)
+ viewLifecycleOwner.lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ homeViewModel.shouldPageForward.collect {
+ if (it) {
+ pageForward()
+ homeViewModel.setShouldPageForward(false)
+ }
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
index e35f51bc3..f34870c2d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -3,28 +3,28 @@
package org.yuzu.yuzu_emu.model
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
class EmulationViewModel : ViewModel() {
- private val _emulationStarted = MutableLiveData(false)
- val emulationStarted: LiveData<Boolean> get() = _emulationStarted
+ val emulationStarted: StateFlow<Boolean> get() = _emulationStarted
+ private val _emulationStarted = MutableStateFlow(false)
- private val _isEmulationStopping = MutableLiveData(false)
- val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping
+ val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping
+ private val _isEmulationStopping = MutableStateFlow(false)
- private val _shaderProgress = MutableLiveData(0)
- val shaderProgress: LiveData<Int> get() = _shaderProgress
+ val shaderProgress: StateFlow<Int> get() = _shaderProgress
+ private val _shaderProgress = MutableStateFlow(0)
- private val _totalShaders = MutableLiveData(0)
- val totalShaders: LiveData<Int> get() = _totalShaders
+ val totalShaders: StateFlow<Int> get() = _totalShaders
+ private val _totalShaders = MutableStateFlow(0)
- private val _shaderMessage = MutableLiveData("")
- val shaderMessage: LiveData<String> get() = _shaderMessage
+ val shaderMessage: StateFlow<String> get() = _shaderMessage
+ private val _shaderMessage = MutableStateFlow("")
fun setEmulationStarted(started: Boolean) {
- _emulationStarted.postValue(started)
+ _emulationStarted.value = started
}
fun setIsEmulationStopping(value: Boolean) {
@@ -50,10 +50,18 @@ class EmulationViewModel : ViewModel() {
}
fun clear() {
- _emulationStarted.value = false
- _isEmulationStopping.value = false
- _shaderProgress.value = 0
- _totalShaders.value = 0
- _shaderMessage.value = ""
+ setEmulationStarted(false)
+ setIsEmulationStopping(false)
+ setShaderProgress(0)
+ setTotalShaders(0)
+ setShaderMessage("")
+ }
+
+ companion object {
+ const val KEY_EMULATION_STARTED = "EmulationStarted"
+ const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting"
+ const val KEY_SHADER_PROGRESS = "ShaderProgress"
+ const val KEY_TOTAL_SHADERS = "TotalShaders"
+ const val KEY_SHADER_MESSAGE = "ShaderMessage"
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index 1fe42f922..6e09fa81d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -5,13 +5,13 @@ package org.yuzu.yuzu_emu.model
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
import java.util.Locale
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
@@ -24,23 +24,23 @@ import org.yuzu.yuzu_emu.utils.GameHelper
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() {
- private val _games = MutableLiveData<List<Game>>(emptyList())
- val games: LiveData<List<Game>> get() = _games
+ val games: StateFlow<List<Game>> get() = _games
+ private val _games = MutableStateFlow(emptyList<Game>())
- private val _searchedGames = MutableLiveData<List<Game>>(emptyList())
- val searchedGames: LiveData<List<Game>> get() = _searchedGames
+ val searchedGames: StateFlow<List<Game>> get() = _searchedGames
+ private val _searchedGames = MutableStateFlow(emptyList<Game>())
- private val _isReloading = MutableLiveData(false)
- val isReloading: LiveData<Boolean> get() = _isReloading
+ val isReloading: StateFlow<Boolean> get() = _isReloading
+ private val _isReloading = MutableStateFlow(false)
- private val _shouldSwapData = MutableLiveData(false)
- val shouldSwapData: LiveData<Boolean> get() = _shouldSwapData
+ val shouldSwapData: StateFlow<Boolean> get() = _shouldSwapData
+ private val _shouldSwapData = MutableStateFlow(false)
- private val _shouldScrollToTop = MutableLiveData(false)
- val shouldScrollToTop: LiveData<Boolean> get() = _shouldScrollToTop
+ val shouldScrollToTop: StateFlow<Boolean> get() = _shouldScrollToTop
+ private val _shouldScrollToTop = MutableStateFlow(false)
- private val _searchFocused = MutableLiveData(false)
- val searchFocused: LiveData<Boolean> get() = _searchFocused
+ val searchFocused: StateFlow<Boolean> get() = _searchFocused
+ private val _searchFocused = MutableStateFlow(false)
init {
// Ensure keys are loaded so that ROM metadata can be decrypted.
@@ -79,36 +79,36 @@ class GamesViewModel : ViewModel() {
)
)
- _games.postValue(sortedList)
+ _games.value = sortedList
}
fun setSearchedGames(games: List<Game>) {
- _searchedGames.postValue(games)
+ _searchedGames.value = games
}
fun setShouldSwapData(shouldSwap: Boolean) {
- _shouldSwapData.postValue(shouldSwap)
+ _shouldSwapData.value = shouldSwap
}
fun setShouldScrollToTop(shouldScroll: Boolean) {
- _shouldScrollToTop.postValue(shouldScroll)
+ _shouldScrollToTop.value = shouldScroll
}
fun setSearchFocused(searchFocused: Boolean) {
- _searchFocused.postValue(searchFocused)
+ _searchFocused.value = searchFocused
}
fun reloadGames(directoryChanged: Boolean) {
- if (isReloading.value == true) {
+ if (isReloading.value) {
return
}
- _isReloading.postValue(true)
+ _isReloading.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
NativeLibrary.resetRomMetadata()
setGames(GameHelper.getGames())
- _isReloading.postValue(false)
+ _isReloading.value = false
if (directoryChanged) {
setShouldSwapData(true)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
index 498c222fa..b32e19373 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
@@ -3,8 +3,8 @@
package org.yuzu.yuzu_emu.model
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
data class HomeSetting(
val titleId: Int,
@@ -14,5 +14,5 @@ data class HomeSetting(
val isEnabled: () -> Boolean = { true },
val disabledTitleId: Int = 0,
val disabledMessageId: Int = 0,
- val details: LiveData<String> = MutableLiveData("")
+ val details: StateFlow<String> = MutableStateFlow("")
)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index a48ef7a88..756f76721 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -5,47 +5,43 @@ package org.yuzu.yuzu_emu.model
import android.net.Uri
import androidx.fragment.app.FragmentActivity
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper
class HomeViewModel : ViewModel() {
- private val _navigationVisible = MutableLiveData<Pair<Boolean, Boolean>>()
- val navigationVisible: LiveData<Pair<Boolean, Boolean>> get() = _navigationVisible
+ val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible
+ private val _navigationVisible = MutableStateFlow(Pair(false, false))
- private val _statusBarShadeVisible = MutableLiveData(true)
- val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
+ val statusBarShadeVisible: StateFlow<Boolean> get() = _statusBarShadeVisible
+ private val _statusBarShadeVisible = MutableStateFlow(true)
- private val _shouldPageForward = MutableLiveData(false)
- val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward
+ val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward
+ private val _shouldPageForward = MutableStateFlow(false)
- private val _gamesDir = MutableLiveData(
+ val gamesDir: StateFlow<String> get() = _gamesDir
+ private val _gamesDir = MutableStateFlow(
Uri.parse(
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getString(GameHelper.KEY_GAME_PATH, "")
).path ?: ""
)
- val gamesDir: LiveData<String> get() = _gamesDir
var navigatedToSetup = false
- init {
- _navigationVisible.value = Pair(false, false)
- }
-
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
- if (_navigationVisible.value?.first == visible) {
+ if (navigationVisible.value.first == visible) {
return
}
_navigationVisible.value = Pair(visible, animated)
}
fun setStatusBarShadeVisibility(visible: Boolean) {
- if (_statusBarShadeVisible.value == visible) {
+ if (statusBarShadeVisible.value == visible) {
return
}
_statusBarShadeVisible.value = visible
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
index d16d15fa6..53fa7a8de 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
@@ -3,48 +3,43 @@
package org.yuzu.yuzu_emu.model
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
-class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
+class SettingsViewModel : ViewModel() {
var game: Game? = null
var shouldSave = false
var clickedItem: SettingsItem? = null
- private val _toolbarTitle = MutableLiveData("")
- val toolbarTitle: LiveData<String> get() = _toolbarTitle
+ val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate
+ private val _shouldRecreate = MutableStateFlow(false)
- private val _shouldRecreate = MutableLiveData(false)
- val shouldRecreate: LiveData<Boolean> get() = _shouldRecreate
+ val shouldNavigateBack: StateFlow<Boolean> get() = _shouldNavigateBack
+ private val _shouldNavigateBack = MutableStateFlow(false)
- private val _shouldNavigateBack = MutableLiveData(false)
- val shouldNavigateBack: LiveData<Boolean> get() = _shouldNavigateBack
+ val shouldShowResetSettingsDialog: StateFlow<Boolean> get() = _shouldShowResetSettingsDialog
+ private val _shouldShowResetSettingsDialog = MutableStateFlow(false)
- private val _shouldShowResetSettingsDialog = MutableLiveData(false)
- val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog
+ val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
+ private val _shouldReloadSettingsList = MutableStateFlow(false)
- private val _shouldReloadSettingsList = MutableLiveData(false)
- val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList
+ val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
+ private val _isUsingSearch = MutableStateFlow(false)
- private val _isUsingSearch = MutableLiveData(false)
- val isUsingSearch: LiveData<Boolean> get() = _isUsingSearch
+ val sliderProgress: StateFlow<Int> get() = _sliderProgress
+ private val _sliderProgress = MutableStateFlow(-1)
- val sliderProgress = savedStateHandle.getStateFlow(KEY_SLIDER_PROGRESS, -1)
+ val sliderTextValue: StateFlow<String> get() = _sliderTextValue
+ private val _sliderTextValue = MutableStateFlow("")
- val sliderTextValue = savedStateHandle.getStateFlow(KEY_SLIDER_TEXT_VALUE, "")
-
- val adapterItemChanged = savedStateHandle.getStateFlow(KEY_ADAPTER_ITEM_CHANGED, -1)
-
- fun setToolbarTitle(value: String) {
- _toolbarTitle.value = value
- }
+ val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged
+ private val _adapterItemChanged = MutableStateFlow(-1)
fun setShouldRecreate(value: Boolean) {
_shouldRecreate.value = value
@@ -67,8 +62,8 @@ class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewMo
}
fun setSliderTextValue(value: Float, units: String) {
- savedStateHandle[KEY_SLIDER_PROGRESS] = value
- savedStateHandle[KEY_SLIDER_TEXT_VALUE] = String.format(
+ _sliderProgress.value = value.toInt()
+ _sliderTextValue.value = String.format(
YuzuApplication.appContext.getString(R.string.value_with_units),
value.toInt().toString(),
units
@@ -76,21 +71,15 @@ class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewMo
}
fun setSliderProgress(value: Float) {
- savedStateHandle[KEY_SLIDER_PROGRESS] = value
+ _sliderProgress.value = value.toInt()
}
fun setAdapterItemChanged(value: Int) {
- savedStateHandle[KEY_ADAPTER_ITEM_CHANGED] = value
+ _adapterItemChanged.value = value
}
fun clear() {
game = null
shouldSave = false
}
-
- companion object {
- const val KEY_SLIDER_TEXT_VALUE = "SliderTextValue"
- const val KEY_SLIDER_PROGRESS = "SliderProgress"
- const val KEY_ADAPTER_ITEM_CHANGED = "AdapterItemChanged"
- }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
index 27ea725a5..531c2aaf0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
@@ -3,29 +3,25 @@
package org.yuzu.yuzu_emu.model
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class TaskViewModel : ViewModel() {
- private val _result = MutableLiveData<Any>()
- val result: LiveData<Any> = _result
+ val result: StateFlow<Any> get() = _result
+ private val _result = MutableStateFlow(Any())
- private val _isComplete = MutableLiveData<Boolean>()
- val isComplete: LiveData<Boolean> = _isComplete
+ val isComplete: StateFlow<Boolean> get() = _isComplete
+ private val _isComplete = MutableStateFlow(false)
- private val _isRunning = MutableLiveData<Boolean>()
- val isRunning: LiveData<Boolean> = _isRunning
+ val isRunning: StateFlow<Boolean> get() = _isRunning
+ private val _isRunning = MutableStateFlow(false)
lateinit var task: () -> Any
- init {
- clear()
- }
-
fun clear() {
_result.value = Any()
_isComplete.value = false
@@ -33,15 +29,16 @@ class TaskViewModel : ViewModel() {
}
fun runTask() {
- if (_isRunning.value == true) {
+ if (isRunning.value) {
return
}
_isRunning.value = true
viewModelScope.launch(Dispatchers.IO) {
val res = task()
- _result.postValue(res)
- _isComplete.postValue(true)
+ _result.value = res
+ _isComplete.value = true
+ _isRunning.value = false
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index b0156dca5..805b89b31 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.ui
+import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -14,8 +15,12 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.color.MaterialColors
import com.google.android.material.transition.MaterialFadeThrough
+import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.GameAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
@@ -44,6 +49,8 @@ class GamesFragment : Fragment() {
return binding.root
}
+ // This is using the correct scope, lint is just acting up
+ @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
homeViewModel.setNavigationVisibility(visible = true, animated = false)
@@ -80,37 +87,48 @@ class GamesFragment : Fragment() {
if (_binding == null) {
return@post
}
- binding.swipeRefresh.isRefreshing = gamesViewModel.isReloading.value!!
+ binding.swipeRefresh.isRefreshing = gamesViewModel.isReloading.value
}
}
- gamesViewModel.apply {
- // Watch for when we get updates to any of our games lists
- isReloading.observe(viewLifecycleOwner) { isReloading ->
- binding.swipeRefresh.isRefreshing = isReloading
+ viewLifecycleOwner.lifecycleScope.apply {
+ launch {
+ repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it }
+ }
}
- games.observe(viewLifecycleOwner) {
- (binding.gridGames.adapter as GameAdapter).submitList(it)
- if (it.isEmpty()) {
- binding.noticeText.visibility = View.VISIBLE
- } else {
- binding.noticeText.visibility = View.GONE
+ launch {
+ repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ gamesViewModel.games.collect {
+ (binding.gridGames.adapter as GameAdapter).submitList(it)
+ if (it.isEmpty()) {
+ binding.noticeText.visibility = View.VISIBLE
+ } else {
+ binding.noticeText.visibility = View.GONE
+ }
+ }
}
}
- shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
- if (shouldSwapData) {
- (binding.gridGames.adapter as GameAdapter).submitList(
- gamesViewModel.games.value!!
- )
- gamesViewModel.setShouldSwapData(false)
+ launch {
+ repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ gamesViewModel.shouldSwapData.collect {
+ if (it) {
+ (binding.gridGames.adapter as GameAdapter).submitList(
+ gamesViewModel.games.value
+ )
+ gamesViewModel.setShouldSwapData(false)
+ }
+ }
}
}
-
- // Check if the user reselected the games menu item and then scroll to top of the list
- shouldScrollToTop.observe(viewLifecycleOwner) { shouldScroll ->
- if (shouldScroll) {
- scrollToTop()
- gamesViewModel.setShouldScrollToTop(false)
+ launch {
+ repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ gamesViewModel.shouldScrollToTop.collect {
+ if (it) {
+ scrollToTop()
+ gamesViewModel.setShouldScrollToTop(false)
+ }
+ }
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 7d8e06ad8..b6b6c6c17 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -19,7 +19,9 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
@@ -40,7 +42,6 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.GamesViewModel
@@ -107,7 +108,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
R.id.homeSettingsFragment -> {
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
- SettingsFile.FILE_NAME_CONFIG
+ Settings.MenuTag.SECTION_ROOT
)
navHostFragment.navController.navigate(action)
}
@@ -115,16 +116,22 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
// Prevents navigation from being drawn for a short time on recreation if set to hidden
- if (!homeViewModel.navigationVisible.value?.first!!) {
+ if (!homeViewModel.navigationVisible.value.first) {
binding.navigationView.visibility = View.INVISIBLE
binding.statusBarShade.visibility = View.INVISIBLE
}
- homeViewModel.navigationVisible.observe(this) {
- showNavigation(it.first, it.second)
- }
- homeViewModel.statusBarShadeVisible.observe(this) { visible ->
- showStatusBarShade(visible)
+ lifecycleScope.apply {
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) }
+ }
+ }
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
+ }
+ }
}
// Dismiss previous notifications (should not happen unless a crash occurred)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
index c0fe596d7..9fe99fab1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
@@ -6,9 +6,11 @@ package org.yuzu.yuzu_emu.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.widget.ImageView
+import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import coil.ImageLoader
import coil.decode.DataSource
+import coil.executeBlocking
import coil.fetch.DrawableResult
import coil.fetch.FetchResult
import coil.fetch.Fetcher
@@ -74,4 +76,13 @@ object GameIconUtils {
.build()
imageLoader.enqueue(request)
}
+
+ fun getGameIcon(game: Game): Bitmap {
+ val request = ImageRequest.Builder(YuzuApplication.appContext)
+ .data(game)
+ .error(R.drawable.default_icon)
+ .build()
+ return imageLoader.executeBlocking(request)
+ .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
+ }
}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 0f2a6d9e4..f31fe054b 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -262,14 +262,12 @@ public:
Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
std::scoped_lock lock(m_mutex);
- // Loads the configuration.
- Config{};
-
// Create the render window.
m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
m_vulkan_library);
m_system.SetFilesystem(m_vfs);
+ m_system.GetUserChannel().clear();
// Initialize system.
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
@@ -329,12 +327,13 @@ public:
m_system.ShutdownMainProcess();
m_detached_tasks.WaitForAllTasks();
m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
+ m_window.reset();
+ OnEmulationStopped(Core::SystemResultStatus::Success);
+ return;
}
// Tear down the render window.
m_window.reset();
-
- OnEmulationStopped(m_load_result);
}
void PauseEmulation() {
@@ -671,18 +670,6 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass claz
return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
- Settings::values.audio_muted = true;
-}
-
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
- Settings::values.audio_muted = false;
-}
-
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
- return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
-}
-
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
return EmulationSession::GetInstance().IsHandheldOnly();
}
diff --git a/src/android/app/src/main/res/drawable/shortcut.xml b/src/android/app/src/main/res/drawable/shortcut.xml
new file mode 100644
index 000000000..c749e5d72
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/shortcut.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <color android:color="@android:color/white" />
+ </item>
+ <item android:id="@+id/shortcut_foreground">
+ <bitmap android:src="@drawable/default_icon" />
+ </item>
+
+</layer-list>
diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml
index c7be37f9b..cfc494b3f 100644
--- a/src/android/app/src/main/res/navigation/emulation_navigation.xml
+++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml
@@ -27,7 +27,7 @@
app:nullable="true" />
<argument
android:name="menuTag"
- app:argType="string" />
+ app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" />
</activity>
<action
diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml
index 2085430bf..2e0ce7a3d 100644
--- a/src/android/app/src/main/res/navigation/home_navigation.xml
+++ b/src/android/app/src/main/res/navigation/home_navigation.xml
@@ -82,7 +82,7 @@
app:nullable="true" />
<argument
android:name="menuTag"
- app:argType="string" />
+ app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" />
</activity>
<action
diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml
index 88e1b4587..1d87d36b3 100644
--- a/src/android/app/src/main/res/navigation/settings_navigation.xml
+++ b/src/android/app/src/main/res/navigation/settings_navigation.xml
@@ -10,7 +10,7 @@
android:label="SettingsFragment">
<argument
android:name="menuTag"
- app:argType="string" />
+ app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" />
<argument
android:name="game"
app:argType="org.yuzu.yuzu_emu.model.Game"
diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml
index 00757e5e8..7b2296d95 100644
--- a/src/android/app/src/main/res/values/dimens.xml
+++ b/src/android/app/src/main/res/values/dimens.xml
@@ -12,6 +12,7 @@
<dimen name="spacing_refresh_end">72dp</dimen>
<dimen name="menu_width">256dp</dimen>
<dimen name="card_width">165dp</dimen>
+ <dimen name="icon_inset">24dp</dimen>
<dimen name="dialog_margin">20dp</dimen>
<dimen name="elevated_app_bar">3dp</dimen>
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 6d2badf76..34877b461 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -151,6 +151,10 @@ add_library(common STATIC
zstd_compression.h
)
+if (YUZU_ENABLE_PORTABLE)
+ add_compile_definitions(YUZU_ENABLE_PORTABLE)
+endif()
+
if (WIN32)
target_sources(common PRIVATE
windows/timer_resolution.cpp
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index d71cfacc6..dce219fcf 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -88,8 +88,9 @@ public:
fs::path yuzu_path_config;
#ifdef _WIN32
+#ifdef YUZU_ENABLE_PORTABLE
yuzu_path = GetExeDirectory() / PORTABLE_DIR;
-
+#endif
if (!IsDir(yuzu_path)) {
yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR;
}
@@ -101,8 +102,9 @@ public:
yuzu_path_cache = yuzu_path / CACHE_DIR;
yuzu_path_config = yuzu_path / CONFIG_DIR;
#else
+#ifdef YUZU_ENABLE_PORTABLE
yuzu_path = GetCurrentDir() / PORTABLE_DIR;
-
+#endif
if (Exists(yuzu_path) && IsDir(yuzu_path)) {
yuzu_path_cache = yuzu_path / CACHE_DIR;
yuzu_path_config = yuzu_path / CONFIG_DIR;
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index c95909561..4e3a614a4 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -112,7 +112,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \
- SUB(Service, NGCT) \
+ SUB(Service, NGC) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NOTIF) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 8356e3183..08af50ee0 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -80,7 +80,7 @@ enum class Class : u8 {
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
- Service_NGCT, ///< The NGCT (No Good Content for Terra) service
+ Service_NGC, ///< The NGC (No Good Content) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NOTIF, ///< The NOTIF (Notification) service
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index b5ef055db..41cbb9ed5 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -19,8 +19,8 @@
namespace Common {
template <typename Condvar, typename Lock, typename Pred>
-void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
- cv.wait(lock, token, std::move(pred));
+void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
+ cv.wait(lk, token, std::move(pred));
}
template <typename Rep, typename Period>
@@ -332,13 +332,17 @@ private:
namespace Common {
template <typename Condvar, typename Lock, typename Pred>
-void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
+void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred pred) {
if (token.stop_requested()) {
return;
}
- std::stop_callback callback(token, [&] { cv.notify_all(); });
- cv.wait(lock, [&] { return pred() || token.stop_requested(); });
+ std::stop_callback callback(token, [&] {
+ { std::scoped_lock lk2{*lk.mutex()}; }
+ cv.notify_all();
+ });
+
+ cv.wait(lk, [&] { return pred() || token.stop_requested(); });
}
template <typename Rep, typename Period>
@@ -353,8 +357,10 @@ bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep,
std::stop_callback cb(token, [&] {
// Wake up the waiting thread.
- std::unique_lock lk{m};
- stop_requested = true;
+ {
+ std::scoped_lock lk{m};
+ stop_requested = true;
+ }
cv.notify_one();
});
diff --git a/src/common/settings.h b/src/common/settings.h
index b15213bd7..82ec9077e 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -348,6 +348,8 @@ struct Values {
Category::RendererDebug};
Setting<bool> disable_shader_loop_safety_checks{
linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
+ Setting<bool> enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey",
+ Category::RendererDebug};
// System
SwitchableSetting<Language, true> language_index{linkage,
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 012648d69..30d2f7df6 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -584,13 +584,23 @@ add_library(core STATIC
hle/service/lm/lm.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
+ hle/service/mii/types/char_info.cpp
+ hle/service/mii/types/char_info.h
+ hle/service/mii/types/core_data.cpp
+ hle/service/mii/types/core_data.h
+ hle/service/mii/types/raw_data.cpp
+ hle/service/mii/types/raw_data.h
+ hle/service/mii/types/store_data.cpp
+ hle/service/mii/types/store_data.h
+ hle/service/mii/types/ver3_store_data.cpp
+ hle/service/mii/types/ver3_store_data.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mii/mii_manager.cpp
hle/service/mii/mii_manager.h
- hle/service/mii/raw_data.cpp
- hle/service/mii/raw_data.h
- hle/service/mii/types.h
+ hle/service/mii/mii_result.h
+ hle/service/mii/mii_types.h
+ hle/service/mii/mii_util.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp
@@ -617,8 +627,8 @@ add_library(core STATIC
hle/service/nfp/nfp_interface.h
hle/service/nfp/nfp_result.h
hle/service/nfp/nfp_types.h
- hle/service/ngct/ngct.cpp
- hle/service/ngct/ngct.h
+ hle/service/ngc/ngc.cpp
+ hle/service/ngc/ngc.h
hle/service/nifm/nifm.cpp
hle/service/nifm/nifm.h
hle/service/nim/nim.cpp
@@ -854,6 +864,8 @@ add_library(core STATIC
telemetry_session.h
tools/freezer.cpp
tools/freezer.h
+ tools/renderdoc.cpp
+ tools/renderdoc.h
)
if (MSVC)
@@ -869,6 +881,7 @@ else()
-Werror=conversion
-Wno-sign-conversion
+ -Wno-cast-function-type
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
)
@@ -877,7 +890,7 @@ endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb)
-target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus)
+target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
endif()
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e95ae80da..e8300cd05 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -51,6 +51,7 @@
#include "core/reporter.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
+#include "core/tools/renderdoc.h"
#include "network/network.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h"
@@ -281,6 +282,10 @@ struct System::Impl {
microprofile_cpu[2] = MICROPROFILE_TOKEN(ARM_CPU2);
microprofile_cpu[3] = MICROPROFILE_TOKEN(ARM_CPU3);
+ if (Settings::values.enable_renderdoc_hotkey) {
+ renderdoc_api = std::make_unique<Tools::RenderdocAPI>();
+ }
+
LOG_DEBUG(Core, "Initialized OK");
return SystemResultStatus::Success;
@@ -406,6 +411,7 @@ struct System::Impl {
gpu_core->NotifyShutdown();
}
+ Network::CancelPendingSocketOperations();
kernel.SuspendApplication(true);
if (services) {
services->KillNVNFlinger();
@@ -427,6 +433,7 @@ struct System::Impl {
debugger.reset();
kernel.Shutdown();
memory.Reset();
+ Network::RestartSocketOperations();
if (auto room_member = room_network.GetRoomMember().lock()) {
Network::GameInfo game_info{};
@@ -519,6 +526,8 @@ struct System::Impl {
std::unique_ptr<Tools::Freezer> memory_freezer;
std::array<u8, 0x20> build_id{};
+ std::unique_ptr<Tools::RenderdocAPI> renderdoc_api;
+
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
@@ -562,6 +571,8 @@ struct System::Impl {
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
gpu_dirty_memory_write_manager{};
+
+ std::deque<std::vector<u8>> user_channel;
};
System::System() : impl{std::make_unique<Impl>(*this)} {}
@@ -1020,6 +1031,10 @@ const Network::RoomNetwork& System::GetRoomNetwork() const {
return impl->room_network;
}
+Tools::RenderdocAPI& System::GetRenderdocAPI() {
+ return *impl->renderdoc_api;
+}
+
void System::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
return impl->kernel.RunServer(std::move(server_manager));
}
@@ -1036,6 +1051,10 @@ void System::ExecuteProgram(std::size_t program_index) {
}
}
+std::deque<std::vector<u8>>& System::GetUserChannel() {
+ return impl->user_channel;
+}
+
void System::RegisterExitCallback(ExitCallback&& callback) {
impl->exit_callback = std::move(callback);
}
diff --git a/src/core/core.h b/src/core/core.h
index a9ff9315e..df20f26f3 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -4,6 +4,7 @@
#pragma once
#include <cstddef>
+#include <deque>
#include <functional>
#include <memory>
#include <mutex>
@@ -101,6 +102,10 @@ namespace Network {
class RoomNetwork;
}
+namespace Tools {
+class RenderdocAPI;
+}
+
namespace Core {
class ARM_Interface;
@@ -412,6 +417,8 @@ public:
/// Gets an immutable reference to the Room Network.
[[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
+ [[nodiscard]] Tools::RenderdocAPI& GetRenderdocAPI();
+
void SetExitLocked(bool locked);
bool GetExitLocked() const;
@@ -459,6 +466,12 @@ public:
*/
void ExecuteProgram(std::size_t program_index);
+ /**
+ * Gets a reference to the user channel stack.
+ * It is used to transfer data between programs.
+ */
+ [[nodiscard]] std::deque<std::vector<u8>>& GetUserChannel();
+
/// Type used for the frontend to designate a callback for System to exit the application.
using ExitCallback = std::function<void()>;
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 52c78020c..f4a774675 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -45,6 +45,10 @@ CNMT::CNMT(CNMTHeader header_, OptionalHeader opt_header_,
CNMT::~CNMT() = default;
+const CNMTHeader& CNMT::GetHeader() const {
+ return header;
+}
+
u64 CNMT::GetTitleID() const {
return header.title_id;
}
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index c59ece010..68e463b5f 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -89,6 +89,7 @@ public:
std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_);
~CNMT();
+ const CNMTHeader& GetHeader() const;
u64 GetTitleID() const;
u32 GetTitleVersion() const;
TitleType GetType() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index f70adab82..e33b00d89 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -9,6 +9,7 @@
#include "common/fs/path_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "common/scope_exit.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/common_funcs.h"
@@ -625,7 +626,7 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
nca->GetTitleId() != title_id) {
// Create fake cnmt for patch to multiprogram application
const auto sub_nca_result =
- InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy);
+ InstallEntry(*nca, cnmt.GetHeader(), record, overwrite_if_exists, copy);
if (sub_nca_result != InstallResult::Success) {
return sub_nca_result;
}
@@ -672,6 +673,31 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
}
+InstallResult RegisteredCache::InstallEntry(const NCA& nca, const CNMTHeader& base_header,
+ const ContentRecord& base_record,
+ bool overwrite_if_exists, const VfsCopyFunction& copy) {
+ const CNMTHeader header{
+ .title_id = nca.GetTitleId(),
+ .title_version = base_header.title_version,
+ .type = base_header.type,
+ .reserved = {},
+ .table_offset = 0x10,
+ .number_content_entries = 1,
+ .number_meta_entries = 0,
+ .attributes = 0,
+ .reserved2 = {},
+ .is_committed = 0,
+ .required_download_system_version = 0,
+ .reserved3 = {},
+ };
+ const OptionalHeader opt_header{0, 0};
+ const CNMT new_cnmt(header, opt_header, {base_record}, {});
+ if (!RawInstallYuzuMeta(new_cnmt)) {
+ return InstallResult::ErrorMetaFailed;
+ }
+ return RawInstallNCA(nca, copy, overwrite_if_exists, base_record.nca_id);
+}
+
bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
bool removed_data = false;
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index bd7f53eaf..64815a845 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -24,6 +24,7 @@ enum class NCAContentType : u8;
enum class TitleType : u8;
struct ContentRecord;
+struct CNMTHeader;
struct MetaRecord;
class RegisteredCache;
@@ -169,6 +170,10 @@ public:
InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
+ InstallResult InstallEntry(const NCA& nca, const CNMTHeader& base_header,
+ const ContentRecord& base_record, bool overwrite_if_exists = false,
+ const VfsCopyFunction& copy = &VfsRawCopy);
+
// Removes an existing entry based on title id
bool RemoveExistingEntry(u64 title_id) const;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4f810d202..8ffdd19e7 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -46,7 +46,7 @@ constexpr Result ResultNoMessages{ErrorModule::AM, 3};
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
enum class LaunchParameterKind : u32 {
- ApplicationSpecific = 1,
+ UserChannel = 1,
AccountPreselectedUser = 2,
};
@@ -1518,27 +1518,26 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto kind = rp.PopEnum<LaunchParameterKind>();
- LOG_DEBUG(Service_AM, "called, kind={:08X}", kind);
-
- if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) {
- const auto backend = BCAT::CreateBackendFromSettings(system, [this](u64 tid) {
- return system.GetFileSystemController().GetBCATDirectory(tid);
- });
- const auto build_id_full = system.GetApplicationProcessBuildID();
- u64 build_id{};
- std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
-
- auto data =
- backend->GetLaunchParameter({system.GetApplicationProcessProgramID(), build_id});
- if (data.has_value()) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IStorage>(system, std::move(*data));
- launch_popped_application_specific = true;
+ LOG_INFO(Service_AM, "called, kind={:08X}", kind);
+
+ if (kind == LaunchParameterKind::UserChannel) {
+ auto channel = system.GetUserChannel();
+ if (channel.empty()) {
+ LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(AM::ResultNoDataInChannel);
return;
}
+
+ auto data = channel.back();
+ channel.pop_back();
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IStorage>(system, std::move(data));
} else if (kind == LaunchParameterKind::AccountPreselectedUser &&
!launch_popped_account_preselect) {
+ // TODO: Verify this is hw-accurate
LaunchParameterAccountPreselectedUser params{};
params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
@@ -1550,7 +1549,6 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
params.current_user = *uuid;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-
rb.Push(ResultSuccess);
std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
@@ -1558,12 +1556,11 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
rb.PushIpcInterface<IStorage>(system, std::move(buffer));
launch_popped_account_preselect = true;
- return;
+ } else {
+ LOG_ERROR(Service_AM, "Unknown launch parameter kind.");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(AM::ResultNoDataInChannel);
}
-
- LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(AM::ResultNoDataInChannel);
}
void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
@@ -1867,14 +1864,22 @@ void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
}
void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called");
+
+ system.GetUserChannel().clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto storage = rp.PopIpcInterface<IStorage>().lock();
+ if (storage) {
+ system.GetUserChannel().push_back(storage->GetData());
+ }
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index d3c2e55ce..f86841c60 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -340,7 +340,6 @@ private:
KernelHelpers::ServiceContext service_context;
- bool launch_popped_application_specific = false;
bool launch_popped_account_preselect = false;
s32 previous_program_index{-1};
Kernel::KEvent* gpu_error_detected_event;
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
index f8e2bac32..350a90818 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.cpp
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -85,15 +85,18 @@ void MiiEdit::Execute() {
break;
case MiiEditAppletMode::CreateMii:
case MiiEditAppletMode::EditMii: {
- Service::Mii::MiiManager mii_manager;
+ Mii::CharInfo char_info{};
+ Mii::StoreData store_data{};
+ store_data.BuildBase(Mii::Gender::Male);
+ char_info.SetFromStoreData(store_data);
- const MiiEditCharInfo char_info{
+ const MiiEditCharInfo edit_char_info{
.mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
? applet_input_v4.char_info.mii_info
- : mii_manager.BuildBase(Mii::Gender::Male)},
+ : char_info},
};
- MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
+ MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
break;
}
default:
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h
index 4705d019f..f3d764073 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit_types.h
+++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h
@@ -7,7 +7,8 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "core/hle/service/mii/types.h"
+#include "common/uuid.h"
+#include "core/hle/service/mii/types/char_info.h"
namespace Service::AM::Applets {
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index 65c11a2f3..3b83c5ed7 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -7,17 +7,16 @@
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h"
+#include "core/hle/service/mii/mii_result.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::Mii {
-constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
-
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
- explicit IDatabaseService(Core::System& system_)
- : ServiceFramework{system_, "IDatabaseService"} {
+ explicit IDatabaseService(Core::System& system_, bool is_system_)
+ : ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
@@ -54,34 +53,27 @@ public:
}
private:
- template <typename T>
- std::vector<u8> SerializeArray(const std::vector<T>& values) {
- std::vector<u8> out(values.size() * sizeof(T));
- std::size_t offset{};
- for (const auto& value : values) {
- std::memcpy(out.data() + offset, &value, sizeof(T));
- offset += sizeof(T);
- }
- return out;
- }
-
void IsUpdated(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
+ const bool is_updated = manager.IsUpdated(metadata, source_flag);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
+ rb.Push<u8>(is_updated);
}
void IsFullDatabase(HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
+ const bool is_full_database = manager.IsFullDatabase();
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(manager.IsFullDatabase());
+ rb.Push<u8>(is_full_database);
}
void GetCount(HLERequestContext& ctx) {
@@ -90,57 +82,63 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
+ const u32 mii_count = manager.GetCount(metadata, source_flag);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(manager.GetCount(source_flag));
+ rb.Push(mii_count);
}
void Get(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
+ const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()};
- LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
+ LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
+
+ u32 mii_count{};
+ std::vector<CharInfoElement> char_info_elements(output_size);
+ Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
- const auto default_miis{manager.GetDefault(source_flag)};
- if (default_miis.size() > 0) {
- ctx.WriteBuffer(SerializeArray(default_miis));
+ if (mii_count != 0) {
+ ctx.WriteBuffer(char_info_elements);
}
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(default_miis.size()));
+ rb.Push(result);
+ rb.Push(mii_count);
}
void Get1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()};
+ const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()};
- LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
+ LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
- const auto default_miis{manager.GetDefault(source_flag)};
+ u32 mii_count{};
+ std::vector<CharInfo> char_info(output_size);
+ Result result = manager.Get(metadata, char_info, mii_count, source_flag);
- std::vector<CharInfo> values;
- for (const auto& element : default_miis) {
- values.emplace_back(element.info);
+ if (mii_count != 0) {
+ ctx.WriteBuffer(char_info);
}
- ctx.WriteBuffer(SerializeArray(values));
-
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(default_miis.size()));
+ rb.Push(result);
+ rb.Push(mii_count);
}
void UpdateLatest(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto info{rp.PopRaw<CharInfo>()};
+ const auto char_info{rp.PopRaw<CharInfo>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
CharInfo new_char_info{};
- const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)};
- if (result != ResultSuccess) {
+ const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
+ if (result.IsFailure()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -153,7 +151,6 @@ private:
void BuildRandom(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
-
const auto age{rp.PopRaw<Age>()};
const auto gender{rp.PopRaw<Gender>()};
const auto race{rp.PopRaw<Race>()};
@@ -162,47 +159,48 @@ private:
if (age > Age::All) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
- LOG_ERROR(Service_Mii, "invalid age={}", age);
+ rb.Push(ResultInvalidArgument);
return;
}
if (gender > Gender::All) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
- LOG_ERROR(Service_Mii, "invalid gender={}", gender);
+ rb.Push(ResultInvalidArgument);
return;
}
if (race > Race::All) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
- LOG_ERROR(Service_Mii, "invalid race={}", race);
+ rb.Push(ResultInvalidArgument);
return;
}
+ CharInfo char_info{};
+ manager.BuildRandom(char_info, age, gender, race);
+
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
- rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race));
+ rb.PushRaw<CharInfo>(char_info);
}
void BuildDefault(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto index{rp.Pop<u32>()};
- LOG_DEBUG(Service_Mii, "called with index={}", index);
+ LOG_INFO(Service_Mii, "called with index={}", index);
if (index > 5) {
- LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
- index);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_INVALID_ARGUMENT);
+ rb.Push(ResultInvalidArgument);
return;
}
+ CharInfo char_info{};
+ manager.BuildDefault(char_info, index);
+
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
- rb.PushRaw<CharInfo>(manager.BuildDefault(index));
+ rb.PushRaw<CharInfo>(char_info);
}
void GetIndex(HLERequestContext& ctx) {
@@ -211,19 +209,21 @@ private:
LOG_DEBUG(Service_Mii, "called");
- u32 index{};
+ s32 index{};
+ const auto result = manager.GetIndex(metadata, info, index);
+
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(manager.GetIndex(info, index));
+ rb.Push(result);
rb.Push(index);
}
void SetInterfaceVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- current_interface_version = rp.PopRaw<u32>();
+ const auto interface_version{rp.PopRaw<u32>()};
- LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version);
+ LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);
- UNIMPLEMENTED_IF(current_interface_version != 1);
+ manager.SetInterfaceVersion(metadata, interface_version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -231,30 +231,27 @@ private:
void Convert(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
-
const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
LOG_INFO(Service_Mii, "called");
+ CharInfo char_info{};
+ manager.ConvertV3ToCharInfo(char_info, mii_v3);
+
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
- rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3));
- }
-
- constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
- return current_interface_version >= interface_version;
+ rb.PushRaw<CharInfo>(char_info);
}
- MiiManager manager;
-
- u32 current_interface_version{};
- u64 current_update_counter{};
+ MiiManager manager{};
+ DatabaseSessionMetadata metadata{};
+ bool is_system{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
public:
- explicit MiiDBModule(Core::System& system_, const char* name_)
- : ServiceFramework{system_, name_} {
+ explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_)
+ : ServiceFramework{system_, name_}, is_system{is_system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
@@ -268,10 +265,12 @@ private:
void GetDatabaseService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDatabaseService>(system);
+ rb.PushIpcInterface<IDatabaseService>(system, is_system);
LOG_DEBUG(Service_Mii, "called");
}
+
+ bool is_system{};
};
class MiiImg final : public ServiceFramework<MiiImg> {
@@ -303,8 +302,10 @@ public:
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
- server_manager->RegisterNamedService("mii:e", std::make_shared<MiiDBModule>(system, "mii:e"));
- server_manager->RegisterNamedService("mii:u", std::make_shared<MiiDBModule>(system, "mii:u"));
+ server_manager->RegisterNamedService("mii:e",
+ std::make_shared<MiiDBModule>(system, "mii:e", true));
+ server_manager->RegisterNamedService("mii:u",
+ std::make_shared<MiiDBModule>(system, "mii:u", false));
server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index dd632df50..292d63777 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -10,385 +10,24 @@
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/mii/mii_manager.h"
-#include "core/hle/service/mii/raw_data.h"
+#include "core/hle/service/mii/mii_result.h"
+#include "core/hle/service/mii/mii_util.h"
+#include "core/hle/service/mii/types/core_data.h"
+#include "core/hle/service/mii/types/raw_data.h"
namespace Service::Mii {
-
-namespace {
-
-constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
-
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
-constexpr MiiStoreData::Name DefaultMiiName{u'n', u'o', u' ', u'n', u'a', u'm', u'e'};
-constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
-constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13};
-constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23};
-constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0};
-constexpr std::array<u8, 62> EyeRotateLookup{
- {0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
- 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
- 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}};
-constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07,
- 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06,
- 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}};
-
-template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
-std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
- std::array<T, DestArraySize> out{};
- std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
- return out;
-}
-
-CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
- MiiStoreBitFields bf;
- std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
-
- return {
- .uuid = data.data.uuid,
- .name = ResizeArray<char16_t, 10, 11>(data.data.name),
- .font_region = static_cast<u8>(bf.font_region.Value()),
- .favorite_color = static_cast<u8>(bf.favorite_color.Value()),
- .gender = static_cast<u8>(bf.gender.Value()),
- .height = static_cast<u8>(bf.height.Value()),
- .build = static_cast<u8>(bf.build.Value()),
- .type = static_cast<u8>(bf.type.Value()),
- .region_move = static_cast<u8>(bf.region_move.Value()),
- .faceline_type = static_cast<u8>(bf.faceline_type.Value()),
- .faceline_color = static_cast<u8>(bf.faceline_color.Value()),
- .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()),
- .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()),
- .hair_type = static_cast<u8>(bf.hair_type.Value()),
- .hair_color = static_cast<u8>(bf.hair_color.Value()),
- .hair_flip = static_cast<u8>(bf.hair_flip.Value()),
- .eye_type = static_cast<u8>(bf.eye_type.Value()),
- .eye_color = static_cast<u8>(bf.eye_color.Value()),
- .eye_scale = static_cast<u8>(bf.eye_scale.Value()),
- .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()),
- .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()),
- .eye_x = static_cast<u8>(bf.eye_x.Value()),
- .eye_y = static_cast<u8>(bf.eye_y.Value()),
- .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()),
- .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()),
- .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()),
- .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()),
- .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()),
- .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()),
- .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3),
- .nose_type = static_cast<u8>(bf.nose_type.Value()),
- .nose_scale = static_cast<u8>(bf.nose_scale.Value()),
- .nose_y = static_cast<u8>(bf.nose_y.Value()),
- .mouth_type = static_cast<u8>(bf.mouth_type.Value()),
- .mouth_color = static_cast<u8>(bf.mouth_color.Value()),
- .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()),
- .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()),
- .mouth_y = static_cast<u8>(bf.mouth_y.Value()),
- .beard_color = static_cast<u8>(bf.beard_color.Value()),
- .beard_type = static_cast<u8>(bf.beard_type.Value()),
- .mustache_type = static_cast<u8>(bf.mustache_type.Value()),
- .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()),
- .mustache_y = static_cast<u8>(bf.mustache_y.Value()),
- .glasses_type = static_cast<u8>(bf.glasses_type.Value()),
- .glasses_color = static_cast<u8>(bf.glasses_color.Value()),
- .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()),
- .glasses_y = static_cast<u8>(bf.glasses_y.Value()),
- .mole_type = static_cast<u8>(bf.mole_type.Value()),
- .mole_scale = static_cast<u8>(bf.mole_scale.Value()),
- .mole_x = static_cast<u8>(bf.mole_x.Value()),
- .mole_y = static_cast<u8>(bf.mole_y.Value()),
- .padding = 0,
- };
-}
-
-u16 GenerateCrc16(const void* data, std::size_t size) {
- s32 crc{};
- for (std::size_t i = 0; i < size; i++) {
- crc ^= static_cast<const u8*>(data)[i] << 8;
- for (std::size_t j = 0; j < 8; j++) {
- crc <<= 1;
- if ((crc & 0x10000) != 0) {
- crc = (crc ^ 0x1021) & 0xFFFF;
- }
- }
- }
- return Common::swap16(static_cast<u16>(crc));
-}
-
-template <typename T>
-T GetRandomValue(T min, T max) {
- std::random_device device;
- std::mt19937 gen(device());
- std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max));
- return static_cast<T>(distribution(gen));
-}
-
-template <typename T>
-T GetRandomValue(T max) {
- return GetRandomValue<T>({}, max);
-}
-
-MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
- MiiStoreBitFields bf{};
-
- if (gender == Gender::All) {
- gender = GetRandomValue<Gender>(Gender::Maximum);
- }
-
- bf.gender.Assign(gender);
- bf.favorite_color.Assign(GetRandomValue<u8>(11));
- bf.region_move.Assign(0);
- bf.font_region.Assign(FontRegion::Standard);
- bf.type.Assign(0);
- bf.height.Assign(64);
- bf.build.Assign(64);
-
- if (age == Age::All) {
- const auto temp{GetRandomValue<int>(10)};
- if (temp >= 8) {
- age = Age::Old;
- } else if (temp >= 4) {
- age = Age::Normal;
- } else {
- age = Age::Young;
- }
- }
-
- if (race == Race::All) {
- const auto temp{GetRandomValue<int>(10)};
- if (temp >= 8) {
- race = Race::Black;
- } else if (temp >= 4) {
- race = Race::White;
- } else {
- race = Race::Asian;
- }
- }
-
- u32 axis_y{};
- if (gender == Gender::Female && age == Age::Young) {
- axis_y = GetRandomValue<u32>(3);
- }
-
- const std::size_t index{3 * static_cast<std::size_t>(age) +
- 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
-
- const auto faceline_type_info{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{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)]);
- bf.faceline_color.Assign(
- faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
- bf.faceline_wrinkle.Assign(
- faceline_wrinkle_info
- .values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
- bf.faceline_makeup.Assign(
- faceline_makeup_info
- .values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
-
- bf.hair_type.Assign(
- hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]);
- bf.hair_color.Assign(
- HairColorLookup[hair_color_info
- .values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]);
- bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum));
-
- bf.eye_type.Assign(
- eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]);
-
- const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
- const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
- const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
- const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]};
-
- bf.eye_color.Assign(
- EyeColorLookup[eye_color_info
- .values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]);
- bf.eye_scale.Assign(4);
- bf.eye_aspect.Assign(3);
- bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate);
- bf.eye_x.Assign(2);
- bf.eye_y.Assign(axis_y + 12);
-
- bf.eyebrow_type.Assign(
- eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
-
- const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
- const auto eyebrow_y{race == Race::Asian ? 9 : 10};
- const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6};
- const auto eyebrow_rotate{
- 32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]};
-
- bf.eyebrow_color.Assign(bf.hair_color);
- bf.eyebrow_scale.Assign(4);
- bf.eyebrow_aspect.Assign(3);
- bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate);
- bf.eyebrow_x.Assign(2);
- bf.eyebrow_y.Assign(axis_y + eyebrow_y);
-
- const auto nose_scale{gender == Gender::Female ? 3 : 4};
-
- bf.nose_type.Assign(
- nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]);
- bf.nose_scale.Assign(nose_scale);
- bf.nose_y.Assign(axis_y + 9);
-
- const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0};
-
- bf.mouth_type.Assign(
- mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
- bf.mouth_color.Assign(MouthColorLookup[mouth_color]);
- bf.mouth_scale.Assign(4);
- bf.mouth_aspect.Assign(3);
- bf.mouth_y.Assign(axis_y + 13);
-
- bf.beard_color.Assign(bf.hair_color);
- bf.mustache_scale.Assign(4);
-
- if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) {
- const auto mustache_and_beard_flag{
- GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)};
-
- auto beard_type{BeardType::None};
- auto mustache_type{MustacheType::None};
-
- if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
- BeardAndMustacheFlag::Beard) {
- beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5);
- }
-
- if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
- BeardAndMustacheFlag::Mustache) {
- mustache_type =
- GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5);
- }
-
- bf.mustache_type.Assign(mustache_type);
- bf.beard_type.Assign(beard_type);
- bf.mustache_y.Assign(10);
- } else {
- bf.mustache_type.Assign(MustacheType::None);
- bf.beard_type.Assign(BeardType::None);
- bf.mustache_y.Assign(axis_y + 10);
- }
-
- const auto glasses_type_start{GetRandomValue<std::size_t>(100)};
- u8 glasses_type{};
- while (glasses_type_start < glasses_type_info.values[glasses_type]) {
- if (++glasses_type >= glasses_type_info.values_count) {
- ASSERT(false);
- break;
- }
- }
-
- bf.glasses_type.Assign(glasses_type);
- bf.glasses_color.Assign(GlassesColorLookup[0]);
- bf.glasses_scale.Assign(4);
- bf.glasses_y.Assign(axis_y + 10);
-
- bf.mole_type.Assign(0);
- bf.mole_scale.Assign(4);
- bf.mole_x.Assign(2);
- bf.mole_y.Assign(20);
+MiiManager::MiiManager() {}
- return {DefaultMiiName, bf, user_id};
-}
-
-MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
- MiiStoreBitFields bf{};
-
- bf.font_region.Assign(info.font_region);
- bf.favorite_color.Assign(info.favorite_color);
- bf.gender.Assign(info.gender);
- bf.height.Assign(info.height);
- bf.build.Assign(info.weight);
- bf.type.Assign(info.type);
- bf.region_move.Assign(info.region);
- bf.faceline_type.Assign(info.face_type);
- bf.faceline_color.Assign(info.face_color);
- bf.faceline_wrinkle.Assign(info.face_wrinkle);
- bf.faceline_makeup.Assign(info.face_makeup);
- bf.hair_type.Assign(info.hair_type);
- bf.hair_color.Assign(HairColorLookup[info.hair_color]);
- bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip));
- bf.eye_type.Assign(info.eye_type);
- bf.eye_color.Assign(EyeColorLookup[info.eye_color]);
- bf.eye_scale.Assign(info.eye_scale);
- bf.eye_aspect.Assign(info.eye_aspect);
- bf.eye_rotate.Assign(info.eye_rotate);
- bf.eye_x.Assign(info.eye_x);
- bf.eye_y.Assign(info.eye_y);
- bf.eyebrow_type.Assign(info.eyebrow_type);
- bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]);
- bf.eyebrow_scale.Assign(info.eyebrow_scale);
- bf.eyebrow_aspect.Assign(info.eyebrow_aspect);
- bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
- bf.eyebrow_x.Assign(info.eyebrow_x);
- bf.eyebrow_y.Assign(info.eyebrow_y - 3);
- bf.nose_type.Assign(info.nose_type);
- bf.nose_scale.Assign(info.nose_scale);
- bf.nose_y.Assign(info.nose_y);
- bf.mouth_type.Assign(info.mouth_type);
- bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]);
- bf.mouth_scale.Assign(info.mouth_scale);
- bf.mouth_aspect.Assign(info.mouth_aspect);
- bf.mouth_y.Assign(info.mouth_y);
- bf.beard_color.Assign(HairColorLookup[info.beard_color]);
- bf.beard_type.Assign(static_cast<BeardType>(info.beard_type));
- bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type));
- bf.mustache_scale.Assign(info.mustache_scale);
- bf.mustache_y.Assign(info.mustache_y);
- bf.glasses_type.Assign(info.glasses_type);
- bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]);
- bf.glasses_scale.Assign(info.glasses_scale);
- bf.glasses_y.Assign(info.glasses_y);
- bf.mole_type.Assign(info.mole_type);
- bf.mole_scale.Assign(info.mole_scale);
- bf.mole_x.Assign(info.mole_x);
- bf.mole_y.Assign(info.mole_y);
-
- return {DefaultMiiName, bf, user_id};
-}
-
-} // namespace
-
-MiiStoreData::MiiStoreData() = default;
-
-MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
- const Common::UUID& user_id) {
- data.name = name;
- data.uuid = Common::UUID::MakeRandomRFC4122V4();
-
- std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
- data_crc = GenerateCrc16(data.data.data(), sizeof(data));
- device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID));
-}
-
-MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {}
-
-bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
+bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return false;
}
- const bool result{current_update_counter != update_counter};
-
- current_update_counter = update_counter;
-
- return result;
+ const auto metadata_update_counter = metadata.update_counter;
+ metadata.update_counter = update_counter;
+ return metadata_update_counter != update_counter;
}
bool MiiManager::IsFullDatabase() const {
@@ -396,306 +35,138 @@ bool MiiManager::IsFullDatabase() const {
return false;
}
-u32 MiiManager::GetCount(SourceFlag source_flag) const {
- std::size_t count{};
+u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
+ u32 mii_count{};
+ if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
+ mii_count += DefaultMiiCount;
+ }
if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
// TODO(bunnei): We don't implement the Mii database, but when we do, update this
- count += 0;
}
- if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
- count += DefaultMiiCount;
- }
- return static_cast<u32>(count);
+ return mii_count;
}
-Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) {
+Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
+ const CharInfo& char_info, SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
- return ERROR_CANNOT_FIND_ENTRY;
+ return ResultNotFound;
}
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
- return ERROR_CANNOT_FIND_ENTRY;
+ return ResultNotFound;
}
-CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
- return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
+void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
+ StoreData store_data{};
+ store_data.BuildDefault(index);
+ out_char_info.SetFromStoreData(store_data);
}
-CharInfo MiiManager::BuildBase(Gender gender) {
- const std::size_t index = gender == Gender::Female ? 1 : 0;
- return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::BaseMii.at(index), user_id));
+void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const {
+ StoreData store_data{};
+ store_data.BuildBase(gender);
+ out_char_info.SetFromStoreData(store_data);
}
-CharInfo MiiManager::BuildDefault(std::size_t index) {
- return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
+void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const {
+ StoreData store_data{};
+ store_data.BuildRandom(age, gender, race);
+ out_char_info.SetFromStoreData(store_data);
}
-CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {
- Service::Mii::MiiManager manager;
- auto mii = manager.BuildBase(Mii::Gender::Male);
+void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
+ StoreData store_data{};
+ mii_v3.BuildToStoreData(store_data);
+ out_char_info.SetFromStoreData(store_data);
+}
- if (!ValidateV3Info(mii_v3)) {
- return mii;
+Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
+ std::span<CharInfoElement> out_elements, u32& out_count,
+ SourceFlag source_flag) {
+ if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+ return BuildDefault(out_elements, out_count, source_flag);
}
- // TODO: We are ignoring a bunch of data from the mii_v3
-
- mii.gender = static_cast<u8>(mii_v3.mii_information.gender);
- mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color);
- mii.height = mii_v3.height;
- mii.build = mii_v3.build;
-
- // Copy name until string terminator
- mii.name = {};
- for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
- mii.name[index] = mii_v3.mii_name[index];
- if (mii.name[index] == 0) {
- break;
- }
- }
+ // TODO(bunnei): We don't implement the Mii database, so we can't have an entry
- mii.font_region = mii_v3.region_information.character_set;
-
- mii.faceline_type = mii_v3.appearance_bits1.face_shape;
- mii.faceline_color = mii_v3.appearance_bits1.skin_color;
- mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles;
- mii.faceline_make = mii_v3.appearance_bits2.makeup;
-
- mii.hair_type = mii_v3.hair_style;
- mii.hair_color = mii_v3.appearance_bits3.hair_color;
- mii.hair_flip = mii_v3.appearance_bits3.flip_hair;
-
- mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type);
- mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color);
- mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale);
- mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch);
- mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation);
- mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing);
- mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position);
-
- mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style);
- mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color);
- mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale);
- mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale);
- mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation);
- mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing);
- mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position);
-
- mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type);
- mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale);
- mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position);
-
- mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type);
- mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color);
- mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale);
- mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch);
- mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position);
-
- mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type);
- mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale);
- mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position);
-
- mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type);
- mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color);
-
- mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type);
- mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color);
- mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale);
- mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position);
-
- mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled);
- mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale);
- mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position);
- mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position);
-
- // TODO: Validate mii data
-
- return mii;
+ // Include default Mii at the end of the list
+ return BuildDefault(out_elements, out_count, source_flag);
}
-Ver3StoreData MiiManager::BuildFromStoreData(const CharInfo& mii) const {
- Service::Mii::MiiManager manager;
- Ver3StoreData mii_v3{};
-
- // TODO: We are ignoring a bunch of data from the mii_v3
-
- mii_v3.version = 1;
- mii_v3.mii_information.gender.Assign(mii.gender);
- mii_v3.mii_information.favorite_color.Assign(mii.favorite_color);
- mii_v3.height = mii.height;
- mii_v3.build = mii.build;
-
- // Copy name until string terminator
- mii_v3.mii_name = {};
- for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
- mii_v3.mii_name[index] = mii.name[index];
- if (mii_v3.mii_name[index] == 0) {
- break;
- }
+Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
+ u32& out_count, SourceFlag source_flag) {
+ if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+ return BuildDefault(out_char_info, out_count, source_flag);
}
- mii_v3.region_information.character_set.Assign(mii.font_region);
-
- mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type);
- mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle);
- mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make);
-
- mii_v3.hair_style = mii.hair_type;
- mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip);
+ // TODO(bunnei): We don't implement the Mii database, so we can't have an entry
- mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type);
- mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
- mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
- mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
- mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x);
- mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
+ // Include default Mii at the end of the list
+ return BuildDefault(out_char_info, out_count, source_flag);
+}
- mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type);
- mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale);
- mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect);
- mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
- mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x);
- mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y);
+Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
+ SourceFlag source_flag) {
+ if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
+ return ResultSuccess;
+ }
- mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type);
- mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale);
- mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y);
+ StoreData store_data{};
- mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type);
- mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
- mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
- mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
+ for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
+ if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
+ return ResultInvalidArgumentSize;
+ }
- mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type);
- mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale);
- mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y);
+ store_data.BuildDefault(static_cast<u32>(index));
- mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type);
+ out_elements[out_count].source = Source::Default;
+ out_elements[out_count].char_info.SetFromStoreData(store_data);
+ out_count++;
+ }
- mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale);
- mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y);
+ return ResultSuccess;
+}
- mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type);
- mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale);
- mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
- mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
+Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
+ SourceFlag source_flag) {
+ if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
+ return ResultSuccess;
+ }
- // These types are converted to V3 from a table
- mii_v3.appearance_bits1.skin_color.Assign(Ver3FacelineColorTable[mii.faceline_color]);
- mii_v3.appearance_bits3.hair_color.Assign(Ver3HairColorTable[mii.hair_color]);
- mii_v3.appearance_bits4.eye_color.Assign(Ver3EyeColorTable[mii.eye_color]);
- mii_v3.appearance_bits5.eyebrow_color.Assign(Ver3HairColorTable[mii.eyebrow_color]);
- mii_v3.appearance_bits7.mouth_color.Assign(Ver3MouthlineColorTable[mii.mouth_color]);
- mii_v3.appearance_bits9.facial_hair_color.Assign(Ver3HairColorTable[mii.beard_color]);
- mii_v3.appearance_bits10.glasses_color.Assign(Ver3GlassColorTable[mii.glasses_color]);
- mii_v3.appearance_bits10.glasses_type.Assign(Ver3GlassTypeTable[mii.glasses_type]);
+ StoreData store_data{};
- mii_v3.crc = GenerateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16));
+ for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
+ if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
+ return ResultInvalidArgumentSize;
+ }
- // TODO: Validate mii_v3 data
+ store_data.BuildDefault(static_cast<u32>(index));
- return mii_v3;
-}
-
-NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const {
- return {
- .faceline_color = static_cast<u8>(mii.faceline_color & 0xf),
- .hair_color = static_cast<u8>(mii.hair_color & 0x7f),
- .eye_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
- .eyebrow_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
- .mouth_color = static_cast<u8>(mii.mouth_color & 0x7f),
- .beard_color = static_cast<u8>(mii.beard_color & 0x7f),
- .glass_color = static_cast<u8>(mii.glasses_color & 0x7f),
- .glass_type = static_cast<u8>(mii.glasses_type & 0x1f),
- };
-}
+ out_char_info[out_count].SetFromStoreData(store_data);
+ out_count++;
+ }
-bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
- bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
-
- is_valid = is_valid && (mii_v3.mii_name[0] != 0);
-
- is_valid = is_valid && (mii_v3.mii_information.birth_month < 13);
- is_valid = is_valid && (mii_v3.mii_information.birth_day < 32);
- is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12);
- is_valid = is_valid && (mii_v3.height < 128);
- is_valid = is_valid && (mii_v3.build < 128);
-
- is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12);
- is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7);
- is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12);
- is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12);
-
- is_valid = is_valid && (mii_v3.hair_style < 132);
- is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8);
-
- is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60);
- is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6);
- is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8);
- is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7);
- is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8);
- is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13);
- is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19);
-
- is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25);
- is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8);
- is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9);
- is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7);
- is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12);
- is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12);
- is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19);
-
- is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18);
- is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9);
- is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19);
-
- is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36);
- is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5);
- is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9);
- is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7);
- is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19);
-
- is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6);
- is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7);
- is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17);
-
- is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6);
- is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8);
-
- is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9);
- is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6);
- is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8);
- is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21);
-
- is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2);
- is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9);
- is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17);
- is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31);
-
- return is_valid;
+ return ResultSuccess;
}
-std::vector<MiiInfoElement> MiiManager::GetDefault(SourceFlag source_flag) {
- std::vector<MiiInfoElement> result;
-
- if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
- return result;
- }
+Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
+ s32& out_index) {
- for (std::size_t index = 0; index < DefaultMiiCount; index++) {
- result.emplace_back(BuildDefault(index), Source::Default);
+ if (char_info.Verify() != ValidationResult::NoErrors) {
+ return ResultInvalidCharInfo;
}
- return result;
-}
-
-Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) {
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
- index = INVALID_INDEX;
+ out_index = INVALID_INDEX;
// TODO(bunnei): We don't implement the Mii database, so we can't have an index
- return ERROR_CANNOT_FIND_ENTRY;
+ return ResultNotFound;
+}
+
+void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) {
+ metadata.interface_version = version;
}
} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 0c8295ebe..a2e7a6d73 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -6,7 +6,10 @@
#include <vector>
#include "core/hle/result.h"
-#include "core/hle/service/mii/types.h"
+#include "core/hle/service/mii/mii_types.h"
+#include "core/hle/service/mii/types/char_info.h"
+#include "core/hle/service/mii/types/store_data.h"
+#include "core/hle/service/mii/types/ver3_store_data.h"
namespace Service::Mii {
@@ -16,26 +19,30 @@ class MiiManager {
public:
MiiManager();
- bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
+ bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
+
bool IsFullDatabase() const;
- u32 GetCount(SourceFlag source_flag) const;
- Result UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag);
- CharInfo BuildRandom(Age age, Gender gender, Race race);
- CharInfo BuildBase(Gender gender);
- CharInfo BuildDefault(std::size_t index);
- CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
- bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
- std::vector<MiiInfoElement> GetDefault(SourceFlag source_flag);
- Result GetIndex(const CharInfo& info, u32& index);
-
- // This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData
- Ver3StoreData BuildFromStoreData(const CharInfo& mii) const;
-
- // This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData
- NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const;
+ u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
+ Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
+ const CharInfo& char_info, SourceFlag source_flag);
+ Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements,
+ u32& out_count, SourceFlag source_flag);
+ Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
+ u32& out_count, SourceFlag source_flag);
+ void BuildDefault(CharInfo& out_char_info, u32 index) const;
+ void BuildBase(CharInfo& out_char_info, Gender gender) const;
+ void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const;
+ void ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const;
+ std::vector<CharInfoElement> GetDefault(SourceFlag source_flag);
+ Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
+ s32& out_index);
+ void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version);
private:
- const Common::UUID user_id{};
+ Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
+ SourceFlag source_flag);
+ Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag);
+
u64 update_counter{};
};
diff --git a/src/core/hle/service/mii/mii_result.h b/src/core/hle/service/mii/mii_result.h
new file mode 100644
index 000000000..021cb76da
--- /dev/null
+++ b/src/core/hle/service/mii/mii_result.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::Mii {
+
+constexpr Result ResultInvalidArgument{ErrorModule::Mii, 1};
+constexpr Result ResultInvalidArgumentSize{ErrorModule::Mii, 2};
+constexpr Result ResultNotUpdated{ErrorModule::Mii, 3};
+constexpr Result ResultNotFound{ErrorModule::Mii, 4};
+constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5};
+constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100};
+constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109};
+constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202};
+constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203};
+
+}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h
new file mode 100644
index 000000000..95476f745
--- /dev/null
+++ b/src/core/hle/service/mii/mii_types.h
@@ -0,0 +1,694 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <type_traits>
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/uuid.h"
+
+namespace Service::Mii {
+
+constexpr u8 MaxHeight = 127;
+constexpr u8 MaxBuild = 127;
+constexpr u8 MaxType = 1;
+constexpr u8 MaxRegionMove = 3;
+constexpr u8 MaxEyeScale = 7;
+constexpr u8 MaxEyeAspect = 6;
+constexpr u8 MaxEyeRotate = 7;
+constexpr u8 MaxEyeX = 12;
+constexpr u8 MaxEyeY = 18;
+constexpr u8 MaxEyebrowScale = 8;
+constexpr u8 MaxEyebrowAspect = 6;
+constexpr u8 MaxEyebrowRotate = 11;
+constexpr u8 MaxEyebrowX = 12;
+constexpr u8 MaxEyebrowY = 18;
+constexpr u8 MaxNoseScale = 8;
+constexpr u8 MaxNoseY = 18;
+constexpr u8 MaxMouthScale = 8;
+constexpr u8 MaxMoutAspect = 6;
+constexpr u8 MaxMouthY = 18;
+constexpr u8 MaxMustacheScale = 8;
+constexpr u8 MasMustacheY = 16;
+constexpr u8 MaxGlassScale = 7;
+constexpr u8 MaxGlassY = 20;
+constexpr u8 MaxMoleScale = 8;
+constexpr u8 MaxMoleX = 16;
+constexpr u8 MaxMoleY = 30;
+constexpr u8 MaxVer3CommonColor = 7;
+constexpr u8 MaxVer3GlassType = 8;
+
+enum class Age : u8 {
+ Young,
+ Normal,
+ Old,
+ All, // Default
+
+ Max = All,
+};
+
+enum class Gender : u8 {
+ Male,
+ Female,
+ All, // Default
+
+ Max = Female,
+};
+
+enum class Race : u8 {
+ Black,
+ White,
+ Asian,
+ All, // Default
+
+ Max = All,
+};
+
+enum class HairType : u8 {
+ NormalLong, // Default
+ NormalShort,
+ NormalMedium,
+ NormalExtraLong,
+ NormalLongBottom,
+ NormalTwoPeaks,
+ PartingLong,
+ FrontLock,
+ PartingShort,
+ PartingExtraLongCurved,
+ PartingExtraLong,
+ PartingMiddleLong,
+ PartingSquared,
+ PartingLongBottom,
+ PeaksTop,
+ PeaksSquared,
+ PartingPeaks,
+ PeaksLongBottom,
+ Peaks,
+ PeaksRounded,
+ PeaksSide,
+ PeaksMedium,
+ PeaksLong,
+ PeaksRoundedLong,
+ PartingFrontPeaks,
+ PartingLongFront,
+ PartingLongRounded,
+ PartingFrontPeaksLong,
+ PartingExtraLongRounded,
+ LongRounded,
+ NormalUnknown1,
+ NormalUnknown2,
+ NormalUnknown3,
+ NormalUnknown4,
+ NormalUnknown5,
+ NormalUnknown6,
+ DreadLocks,
+ PlatedMats,
+ Caps,
+ Afro,
+ PlatedMatsLong,
+ Beanie,
+ Short,
+ ShortTopLongSide,
+ ShortUnknown1,
+ ShortUnknown2,
+ MilitaryParting,
+ Military,
+ ShortUnknown3,
+ ShortUnknown4,
+ ShortUnknown5,
+ ShortUnknown6,
+ NoneTop,
+ None,
+ LongUnknown1,
+ LongUnknown2,
+ LongUnknown3,
+ LongUnknown4,
+ LongUnknown5,
+ LongUnknown6,
+ LongUnknown7,
+ LongUnknown8,
+ LongUnknown9,
+ LongUnknown10,
+ LongUnknown11,
+ LongUnknown12,
+ LongUnknown13,
+ LongUnknown14,
+ LongUnknown15,
+ LongUnknown16,
+ LongUnknown17,
+ LongUnknown18,
+ LongUnknown19,
+ LongUnknown20,
+ LongUnknown21,
+ LongUnknown22,
+ LongUnknown23,
+ LongUnknown24,
+ LongUnknown25,
+ LongUnknown26,
+ LongUnknown27,
+ LongUnknown28,
+ LongUnknown29,
+ LongUnknown30,
+ LongUnknown31,
+ LongUnknown32,
+ LongUnknown33,
+ LongUnknown34,
+ LongUnknown35,
+ LongUnknown36,
+ LongUnknown37,
+ LongUnknown38,
+ LongUnknown39,
+ LongUnknown40,
+ LongUnknown41,
+ LongUnknown42,
+ LongUnknown43,
+ LongUnknown44,
+ LongUnknown45,
+ LongUnknown46,
+ LongUnknown47,
+ LongUnknown48,
+ LongUnknown49,
+ LongUnknown50,
+ LongUnknown51,
+ LongUnknown52,
+ LongUnknown53,
+ LongUnknown54,
+ LongUnknown55,
+ LongUnknown56,
+ LongUnknown57,
+ LongUnknown58,
+ LongUnknown59,
+ LongUnknown60,
+ LongUnknown61,
+ LongUnknown62,
+ LongUnknown63,
+ LongUnknown64,
+ LongUnknown65,
+ LongUnknown66,
+ TwoMediumFrontStrandsOneLongBackPonyTail,
+ TwoFrontStrandsLongBackPonyTail,
+ PartingFrontTwoLongBackPonyTails,
+ TwoFrontStrandsOneLongBackPonyTail,
+ LongBackPonyTail,
+ LongFrontTwoLongBackPonyTails,
+ StrandsTwoShortSidedPonyTails,
+ TwoMediumSidedPonyTails,
+ ShortFrontTwoBackPonyTails,
+ TwoShortSidedPonyTails,
+ TwoLongSidedPonyTails,
+ LongFrontTwoBackPonyTails,
+
+ Max = LongFrontTwoBackPonyTails,
+};
+
+enum class MoleType : u8 {
+ None, // Default
+ OneDot,
+
+ Max = OneDot,
+};
+
+enum class HairFlip : u8 {
+ Left, // Default
+ Right,
+
+ Max = Right,
+};
+
+enum class CommonColor : u8 {
+ // For simplicity common colors aren't listed
+ Max = 99,
+ Count = 100,
+};
+
+enum class FavoriteColor : u8 {
+ Red, // Default
+ Orange,
+ Yellow,
+ LimeGreen,
+ Green,
+ Blue,
+ LightBlue,
+ Pink,
+ Purple,
+ Brown,
+ White,
+ Black,
+
+ Max = Black,
+};
+
+enum class EyeType : u8 {
+ Normal, // Default
+ NormalLash,
+ WhiteLash,
+ WhiteNoBottom,
+ OvalAngledWhite,
+ AngryWhite,
+ DotLashType1,
+ Line,
+ DotLine,
+ OvalWhite,
+ RoundedWhite,
+ NormalShadow,
+ CircleWhite,
+ Circle,
+ CircleWhiteStroke,
+ NormalOvalNoBottom,
+ NormalOvalLarge,
+ NormalRoundedNoBottom,
+ SmallLash,
+ Small,
+ TwoSmall,
+ NormalLongLash,
+ WhiteTwoLashes,
+ WhiteThreeLashes,
+ DotAngry,
+ DotAngled,
+ Oval,
+ SmallWhite,
+ WhiteAngledNoBottom,
+ WhiteAngledNoLeft,
+ SmallWhiteTwoLashes,
+ LeafWhiteLash,
+ WhiteLargeNoBottom,
+ Dot,
+ DotLashType2,
+ DotThreeLashes,
+ WhiteOvalTop,
+ WhiteOvalBottom,
+ WhiteOvalBottomFlat,
+ WhiteOvalTwoLashes,
+ WhiteOvalThreeLashes,
+ WhiteOvalNoBottomTwoLashes,
+ DotWhite,
+ WhiteOvalTopFlat,
+ WhiteThinLeaf,
+ StarThreeLashes,
+ LineTwoLashes,
+ CrowsFeet,
+ WhiteNoBottomFlat,
+ WhiteNoBottomRounded,
+ WhiteSmallBottomLine,
+ WhiteNoBottomLash,
+ WhiteNoPartialBottomLash,
+ WhiteOvalBottomLine,
+ WhiteNoBottomLashTopLine,
+ WhiteNoPartialBottomTwoLashes,
+ NormalTopLine,
+ WhiteOvalLash,
+ RoundTired,
+ WhiteLarge,
+
+ Max = WhiteLarge,
+};
+
+enum class MouthType : u8 {
+ Neutral, // Default
+ NeutralLips,
+ Smile,
+ SmileStroke,
+ SmileTeeth,
+ LipsSmall,
+ LipsLarge,
+ Wave,
+ WaveAngrySmall,
+ NeutralStrokeLarge,
+ TeethSurprised,
+ LipsExtraLarge,
+ LipsUp,
+ NeutralDown,
+ Surprised,
+ TeethMiddle,
+ NeutralStroke,
+ LipsExtraSmall,
+ Malicious,
+ LipsDual,
+ NeutralComma,
+ NeutralUp,
+ TeethLarge,
+ WaveAngry,
+ LipsSexy,
+ SmileInverted,
+ LipsSexyOutline,
+ SmileRounded,
+ LipsTeeth,
+ NeutralOpen,
+ TeethRounded,
+ WaveAngrySmallInverted,
+ NeutralCommaInverted,
+ TeethFull,
+ SmileDownLine,
+ Kiss,
+
+ Max = Kiss,
+};
+
+enum class FontRegion : u8 {
+ Standard, // Default
+ China,
+ Korea,
+ Taiwan,
+
+ Max = Taiwan,
+};
+
+enum class FacelineType : u8 {
+ Sharp, // Default
+ Rounded,
+ SharpRounded,
+ SharpRoundedSmall,
+ Large,
+ LargeRounded,
+ SharpSmall,
+ Flat,
+ Bump,
+ Angular,
+ FlatRounded,
+ AngularSmall,
+
+ Max = AngularSmall,
+};
+
+enum class FacelineColor : u8 {
+ Beige, // Default
+ WarmBeige,
+ Natural,
+ Honey,
+ Chestnut,
+ Porcelain,
+ Ivory,
+ WarmIvory,
+ Almond,
+ Espresso,
+
+ Max = Espresso,
+ Count = Max + 1,
+};
+
+enum class FacelineWrinkle : u8 {
+ None, // Default
+ TearTroughs,
+ FacialPain,
+ Cheeks,
+ Folds,
+ UnderTheEyes,
+ SplitChin,
+ Chin,
+ BrowDroop,
+ MouthFrown,
+ CrowsFeet,
+ FoldsCrowsFrown,
+
+ Max = FoldsCrowsFrown,
+};
+
+enum class FacelineMake : u8 {
+ None, // Default
+ CheekPorcelain,
+ CheekNatural,
+ EyeShadowBlue,
+ CheekBlushPorcelain,
+ CheekBlushNatural,
+ CheekPorcelainEyeShadowBlue,
+ CheekPorcelainEyeShadowNatural,
+ CheekBlushPorcelainEyeShadowEspresso,
+ Freckles,
+ LionsManeBeard,
+ StubbleBeard,
+
+ Max = StubbleBeard,
+};
+
+enum class EyebrowType : u8 {
+ FlatAngledLarge, // Default
+ LowArchRoundedThin,
+ SoftAngledLarge,
+ MediumArchRoundedThin,
+ RoundedMedium,
+ LowArchMedium,
+ RoundedThin,
+ UpThin,
+ MediumArchRoundedMedium,
+ RoundedLarge,
+ UpLarge,
+ FlatAngledLargeInverted,
+ MediumArchFlat,
+ AngledThin,
+ HorizontalLarge,
+ HighArchFlat,
+ Flat,
+ MediumArchLarge,
+ LowArchThin,
+ RoundedThinInverted,
+ HighArchLarge,
+ Hairy,
+ Dotted,
+ None,
+
+ Max = None,
+};
+
+enum class NoseType : u8 {
+ Normal, // Default
+ Rounded,
+ Dot,
+ Arrow,
+ Roman,
+ Triangle,
+ Button,
+ RoundedInverted,
+ Potato,
+ Grecian,
+ Snub,
+ Aquiline,
+ ArrowLeft,
+ RoundedLarge,
+ Hooked,
+ Fat,
+ Droopy,
+ ArrowLarge,
+
+ Max = ArrowLarge,
+};
+
+enum class BeardType : u8 {
+ None,
+ Goatee,
+ GoateeLong,
+ LionsManeLong,
+ LionsMane,
+ Full,
+
+ Min = Goatee,
+ Max = Full,
+};
+
+enum class MustacheType : u8 {
+ None,
+ Walrus,
+ Pencil,
+ Horseshoe,
+ Normal,
+ Toothbrush,
+
+ Min = Walrus,
+ Max = Toothbrush,
+};
+
+enum class GlassType : u8 {
+ None,
+ Oval,
+ Wayfarer,
+ Rectangle,
+ TopRimless,
+ Rounded,
+ Oversized,
+ CatEye,
+ Square,
+ BottomRimless,
+ SemiOpaqueRounded,
+ SemiOpaqueCatEye,
+ SemiOpaqueOval,
+ SemiOpaqueRectangle,
+ SemiOpaqueAviator,
+ OpaqueRounded,
+ OpaqueCatEye,
+ OpaqueOval,
+ OpaqueRectangle,
+ OpaqueAviator,
+
+ Max = OpaqueAviator,
+ Count = Max + 1,
+};
+
+enum class BeardAndMustacheFlag : u32 {
+ Beard = 1,
+ Mustache,
+ All = Beard | Mustache,
+};
+DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
+
+enum class Source : u32 {
+ Database = 0,
+ Default = 1,
+ Account = 2,
+ Friend = 3,
+};
+
+enum class SourceFlag : u32 {
+ None = 0,
+ Database = 1 << 0,
+ Default = 1 << 1,
+};
+DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
+
+enum class ValidationResult : u32 {
+ NoErrors = 0x0,
+ InvalidBeardColor = 0x1,
+ InvalidBeardType = 0x2,
+ InvalidBuild = 0x3,
+ InvalidEyeAspect = 0x4,
+ InvalidEyeColor = 0x5,
+ InvalidEyeRotate = 0x6,
+ InvalidEyeScale = 0x7,
+ InvalidEyeType = 0x8,
+ InvalidEyeX = 0x9,
+ InvalidEyeY = 0xa,
+ InvalidEyebrowAspect = 0xb,
+ InvalidEyebrowColor = 0xc,
+ InvalidEyebrowRotate = 0xd,
+ InvalidEyebrowScale = 0xe,
+ InvalidEyebrowType = 0xf,
+ InvalidEyebrowX = 0x10,
+ InvalidEyebrowY = 0x11,
+ InvalidFacelineColor = 0x12,
+ InvalidFacelineMake = 0x13,
+ InvalidFacelineWrinkle = 0x14,
+ InvalidFacelineType = 0x15,
+ InvalidColor = 0x16,
+ InvalidFont = 0x17,
+ InvalidGender = 0x18,
+ InvalidGlassColor = 0x19,
+ InvalidGlassScale = 0x1a,
+ InvalidGlassType = 0x1b,
+ InvalidGlassY = 0x1c,
+ InvalidHairColor = 0x1d,
+ InvalidHairFlip = 0x1e,
+ InvalidHairType = 0x1f,
+ InvalidHeight = 0x20,
+ InvalidMoleScale = 0x21,
+ InvalidMoleType = 0x22,
+ InvalidMoleX = 0x23,
+ InvalidMoleY = 0x24,
+ InvalidMouthAspect = 0x25,
+ InvalidMouthColor = 0x26,
+ InvalidMouthScale = 0x27,
+ InvalidMouthType = 0x28,
+ InvalidMouthY = 0x29,
+ InvalidMustacheScale = 0x2a,
+ InvalidMustacheType = 0x2b,
+ InvalidMustacheY = 0x2c,
+ InvalidNoseScale = 0x2e,
+ InvalidNoseType = 0x2f,
+ InvalidNoseY = 0x30,
+ InvalidRegionMove = 0x31,
+ InvalidCreateId = 0x32,
+ InvalidName = 0x33,
+ InvalidType = 0x35,
+};
+
+struct Nickname {
+ static constexpr std::size_t MaxNameSize = 10;
+ std::array<char16_t, MaxNameSize> data;
+
+ // Checks for null, non-zero terminated or dirty strings
+ bool IsValid() const {
+ if (data[0] == 0) {
+ return false;
+ }
+
+ if (data[MaxNameSize] != 0) {
+ return false;
+ }
+ std::size_t index = 1;
+ while (data[index] != 0) {
+ index++;
+ }
+ while (index < MaxNameSize && data[index] == 0) {
+ index++;
+ }
+ return index == MaxNameSize;
+ }
+};
+static_assert(sizeof(Nickname) == 0x14, "Nickname is an invalid size");
+
+struct DefaultMii {
+ u32 face_type{};
+ u32 face_color{};
+ u32 face_wrinkle{};
+ u32 face_makeup{};
+ u32 hair_type{};
+ u32 hair_color{};
+ u32 hair_flip{};
+ u32 eye_type{};
+ u32 eye_color{};
+ u32 eye_scale{};
+ u32 eye_aspect{};
+ u32 eye_rotate{};
+ u32 eye_x{};
+ u32 eye_y{};
+ u32 eyebrow_type{};
+ u32 eyebrow_color{};
+ u32 eyebrow_scale{};
+ u32 eyebrow_aspect{};
+ u32 eyebrow_rotate{};
+ u32 eyebrow_x{};
+ u32 eyebrow_y{};
+ u32 nose_type{};
+ u32 nose_scale{};
+ u32 nose_y{};
+ u32 mouth_type{};
+ u32 mouth_color{};
+ u32 mouth_scale{};
+ u32 mouth_aspect{};
+ u32 mouth_y{};
+ u32 mustache_type{};
+ u32 beard_type{};
+ u32 beard_color{};
+ u32 mustache_scale{};
+ u32 mustache_y{};
+ u32 glasses_type{};
+ u32 glasses_color{};
+ u32 glasses_scale{};
+ u32 glasses_y{};
+ u32 mole_type{};
+ u32 mole_scale{};
+ u32 mole_x{};
+ u32 mole_y{};
+ u32 height{};
+ u32 weight{};
+ u32 gender{};
+ u32 favorite_color{};
+ u32 region_move{};
+ u32 font_region{};
+ u32 type{};
+ Nickname nickname;
+};
+static_assert(sizeof(DefaultMii) == 0xd8, "DefaultMii has incorrect size.");
+
+struct DatabaseSessionMetadata {
+ u32 interface_version;
+ u32 magic;
+ u64 update_counter;
+
+ bool IsInterfaceVersionSupported(u32 version) const {
+ return version <= interface_version;
+ }
+};
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h
new file mode 100644
index 000000000..ddb544c23
--- /dev/null
+++ b/src/core/hle/service/mii/mii_util.h
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <random>
+#include <span>
+
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "common/uuid.h"
+#include "core/hle/service/mii/mii_types.h"
+
+namespace Service::Mii {
+class MiiUtil {
+public:
+ static u16 CalculateCrc16(const void* data, std::size_t size) {
+ s32 crc{};
+ for (std::size_t i = 0; i < size; i++) {
+ crc ^= static_cast<const u8*>(data)[i] << 8;
+ for (std::size_t j = 0; j < 8; j++) {
+ crc <<= 1;
+ if ((crc & 0x10000) != 0) {
+ crc = (crc ^ 0x1021) & 0xFFFF;
+ }
+ }
+ }
+ return Common::swap16(static_cast<u16>(crc));
+ }
+
+ static Common::UUID MakeCreateId() {
+ return Common::UUID::MakeRandomRFC4122V4();
+ }
+
+ static Common::UUID GetDeviceId() {
+ // This should be nn::settings::detail::GetMiiAuthorId()
+ return Common::UUID::MakeDefault();
+ }
+
+ template <typename T>
+ static T GetRandomValue(T min, T max) {
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<u64> distribution(static_cast<u64>(min),
+ static_cast<u64>(max));
+ return static_cast<T>(distribution(gen));
+ }
+
+ template <typename T>
+ static T GetRandomValue(T max) {
+ return GetRandomValue<T>({}, max);
+ }
+
+ static bool IsFontRegionValid(FontRegion font, std::span<const char16_t> text) {
+ // TODO: This function needs to check against the font tables
+ return true;
+ }
+};
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
deleted file mode 100644
index cdd2337d6..000000000
--- a/src/core/hle/service/mii/raw_data.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <array>
-
-#include "core/hle/service/mii/types.h"
-
-namespace Service::Mii::RawData {
-
-extern const std::array<Service::Mii::DefaultMii, 2> BaseMii;
-extern const std::array<Service::Mii::DefaultMii, 6> 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/mii/types.h b/src/core/hle/service/mii/types.h
deleted file mode 100644
index c48d08d79..000000000
--- a/src/core/hle/service/mii/types.h
+++ /dev/null
@@ -1,553 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <array>
-#include <type_traits>
-
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/uuid.h"
-
-namespace Service::Mii {
-
-enum class Age : u32 {
- Young,
- Normal,
- Old,
- All,
-};
-
-enum class BeardType : u32 {
- None,
- Beard1,
- Beard2,
- Beard3,
- Beard4,
- Beard5,
-};
-
-enum class BeardAndMustacheFlag : u32 {
- Beard = 1,
- Mustache,
- All = Beard | Mustache,
-};
-DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
-
-enum class FontRegion : u32 {
- Standard,
- China,
- Korea,
- Taiwan,
-};
-
-enum class Gender : u32 {
- Male,
- Female,
- All,
- Maximum = Female,
-};
-
-enum class HairFlip : u32 {
- Left,
- Right,
- Maximum = Right,
-};
-
-enum class MustacheType : u32 {
- None,
- Mustache1,
- Mustache2,
- Mustache3,
- Mustache4,
- Mustache5,
-};
-
-enum class Race : u32 {
- Black,
- White,
- Asian,
- All,
-};
-
-enum class Source : u32 {
- Database = 0,
- Default = 1,
- Account = 2,
- Friend = 3,
-};
-
-enum class SourceFlag : u32 {
- None = 0,
- Database = 1 << 0,
- Default = 1 << 1,
-};
-DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
-
-// nn::mii::CharInfo
-struct CharInfo {
- 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;
-};
-static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
-static_assert(std::has_unique_object_representations_v<CharInfo>,
- "All bits of CharInfo must contribute to its value.");
-
-#pragma pack(push, 4)
-
-struct MiiInfoElement {
- MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {}
-
- CharInfo info{};
- Source source{};
-};
-static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
-
-struct MiiStoreBitFields {
- union {
- u32 word_0{};
-
- BitField<0, 8, u32> hair_type;
- BitField<8, 7, u32> height;
- BitField<15, 1, u32> mole_type;
- BitField<16, 7, u32> build;
- BitField<23, 1, HairFlip> hair_flip;
- BitField<24, 7, u32> hair_color;
- BitField<31, 1, u32> type;
- };
-
- union {
- u32 word_1{};
-
- BitField<0, 7, u32> eye_color;
- BitField<7, 1, Gender> gender;
- BitField<8, 7, u32> eyebrow_color;
- BitField<16, 7, u32> mouth_color;
- BitField<24, 7, u32> beard_color;
- };
-
- union {
- u32 word_2{};
-
- BitField<0, 7, u32> glasses_color;
- BitField<8, 6, u32> eye_type;
- BitField<14, 2, u32> region_move;
- BitField<16, 6, u32> mouth_type;
- BitField<22, 2, FontRegion> font_region;
- BitField<24, 5, u32> eye_y;
- BitField<29, 3, u32> glasses_scale;
- };
-
- union {
- u32 word_3{};
-
- BitField<0, 5, u32> eyebrow_type;
- BitField<5, 3, MustacheType> mustache_type;
- BitField<8, 5, u32> nose_type;
- BitField<13, 3, BeardType> beard_type;
- BitField<16, 5, u32> nose_y;
- BitField<21, 3, u32> mouth_aspect;
- BitField<24, 5, u32> mouth_y;
- BitField<29, 3, u32> eyebrow_aspect;
- };
-
- union {
- u32 word_4{};
-
- BitField<0, 5, u32> mustache_y;
- BitField<5, 3, u32> eye_rotate;
- BitField<8, 5, u32> glasses_y;
- BitField<13, 3, u32> eye_aspect;
- BitField<16, 5, u32> mole_x;
- BitField<21, 3, u32> eye_scale;
- BitField<24, 5, u32> mole_y;
- };
-
- union {
- u32 word_5{};
-
- BitField<0, 5, u32> glasses_type;
- BitField<8, 4, u32> favorite_color;
- BitField<12, 4, u32> faceline_type;
- BitField<16, 4, u32> faceline_color;
- BitField<20, 4, u32> faceline_wrinkle;
- BitField<24, 4, u32> faceline_makeup;
- BitField<28, 4, u32> eye_x;
- };
-
- union {
- u32 word_6{};
-
- BitField<0, 4, u32> eyebrow_scale;
- BitField<4, 4, u32> eyebrow_rotate;
- BitField<8, 4, u32> eyebrow_x;
- BitField<12, 4, u32> eyebrow_y;
- BitField<16, 4, u32> nose_scale;
- BitField<20, 4, u32> mouth_scale;
- BitField<24, 4, u32> mustache_scale;
- BitField<28, 4, u32> mole_scale;
- };
-};
-static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
-static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
- "MiiStoreBitFields is not trivially copyable.");
-
-// This is nn::mii::Ver3StoreData
-// Based on citra HLE::Applets::MiiData and PretendoNetwork.
-// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
-// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
-struct Ver3StoreData {
- u8 version;
- union {
- u8 raw;
-
- BitField<0, 1, u8> allow_copying;
- BitField<1, 1, u8> profanity_flag;
- BitField<2, 2, u8> region_lock;
- BitField<4, 2, u8> character_set;
- } region_information;
- u16_be mii_id;
- u64_be system_id;
- u32_be specialness_and_creation_date;
- std::array<u8, 0x6> creator_mac;
- u16_be padding;
- union {
- u16 raw;
-
- BitField<0, 1, u16> gender;
- BitField<1, 4, u16> birth_month;
- BitField<5, 5, u16> birth_day;
- BitField<10, 4, u16> favorite_color;
- BitField<14, 1, u16> favorite;
- } mii_information;
- std::array<char16_t, 0xA> mii_name;
- u8 height;
- u8 build;
- union {
- u8 raw;
-
- BitField<0, 1, u8> disable_sharing;
- BitField<1, 4, u8> face_shape;
- BitField<5, 3, u8> skin_color;
- } appearance_bits1;
- union {
- u8 raw;
-
- BitField<0, 4, u8> wrinkles;
- BitField<4, 4, u8> makeup;
- } appearance_bits2;
- u8 hair_style;
- union {
- u8 raw;
-
- BitField<0, 3, u8> hair_color;
- BitField<3, 1, u8> flip_hair;
- } appearance_bits3;
- union {
- u32 raw;
-
- BitField<0, 6, u32> eye_type;
- BitField<6, 3, u32> eye_color;
- BitField<9, 4, u32> eye_scale;
- BitField<13, 3, u32> eye_vertical_stretch;
- BitField<16, 5, u32> eye_rotation;
- BitField<21, 4, u32> eye_spacing;
- BitField<25, 5, u32> eye_y_position;
- } appearance_bits4;
- union {
- u32 raw;
-
- BitField<0, 5, u32> eyebrow_style;
- BitField<5, 3, u32> eyebrow_color;
- BitField<8, 4, u32> eyebrow_scale;
- BitField<12, 3, u32> eyebrow_yscale;
- BitField<16, 4, u32> eyebrow_rotation;
- BitField<21, 4, u32> eyebrow_spacing;
- BitField<25, 5, u32> eyebrow_y_position;
- } appearance_bits5;
- union {
- u16 raw;
-
- BitField<0, 5, u16> nose_type;
- BitField<5, 4, u16> nose_scale;
- BitField<9, 5, u16> nose_y_position;
- } appearance_bits6;
- union {
- u16 raw;
-
- BitField<0, 6, u16> mouth_type;
- BitField<6, 3, u16> mouth_color;
- BitField<9, 4, u16> mouth_scale;
- BitField<13, 3, u16> mouth_horizontal_stretch;
- } appearance_bits7;
- union {
- u8 raw;
-
- BitField<0, 5, u8> mouth_y_position;
- BitField<5, 3, u8> mustache_type;
- } appearance_bits8;
- u8 allow_copying;
- union {
- u16 raw;
-
- BitField<0, 3, u16> bear_type;
- BitField<3, 3, u16> facial_hair_color;
- BitField<6, 4, u16> mustache_scale;
- BitField<10, 5, u16> mustache_y_position;
- } appearance_bits9;
- union {
- u16 raw;
-
- BitField<0, 4, u16> glasses_type;
- BitField<4, 3, u16> glasses_color;
- BitField<7, 4, u16> glasses_scale;
- BitField<11, 5, u16> glasses_y_position;
- } appearance_bits10;
- union {
- u16 raw;
-
- BitField<0, 1, u16> mole_enabled;
- BitField<1, 4, u16> mole_scale;
- BitField<5, 5, u16> mole_x_position;
- BitField<10, 5, u16> mole_y_position;
- } appearance_bits11;
-
- std::array<u16_le, 0xA> author_name;
- INSERT_PADDING_BYTES(0x2);
- u16_be crc;
-};
-static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
-
-struct NfpStoreDataExtension {
- u8 faceline_color;
- u8 hair_color;
- u8 eye_color;
- u8 eyebrow_color;
- u8 mouth_color;
- u8 beard_color;
- u8 glass_color;
- u8 glass_type;
-};
-static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size");
-
-constexpr std::array<u8, 0x10> Ver3FacelineColorTable{
- 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5,
-};
-
-constexpr std::array<u8, 100> Ver3HairColorTable{
- 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0,
- 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
- 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4,
- 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5,
- 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7,
- 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4,
-};
-
-constexpr std::array<u8, 100> Ver3EyeColorTable{
- 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4,
- 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
- 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4,
- 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
- 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2,
- 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
-};
-
-constexpr std::array<u8, 100> Ver3MouthlineColorTable{
- 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
- 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
- 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
- 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
- 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3,
- 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3,
-};
-
-constexpr std::array<u8, 100> Ver3GlassColorTable{
- 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3,
- 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
- 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
- 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5,
- 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4,
- 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
-};
-
-constexpr std::array<u8, 20> Ver3GlassTypeTable{
- 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1,
- 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7,
-};
-
-struct MiiStoreData {
- using Name = std::array<char16_t, 10>;
-
- MiiStoreData();
- MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
- const Common::UUID& user_id);
-
- // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
- // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
- // not suitable for our uses.
- struct {
- std::array<u8, 0x1C> data{};
- static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
-
- Name name{};
- Common::UUID uuid{};
- } data;
-
- u16 data_crc{};
- u16 device_crc{};
-};
-static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
-
-struct MiiStoreDataElement {
- MiiStoreData data{};
- Source source{};
-};
-static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
-
-struct MiiDatabase {
- u32 magic{}; // 'NFDB'
- std::array<MiiStoreData, 0x64> miis{};
- INSERT_PADDING_BYTES(1);
- u8 count{};
- u16 crc{};
-};
-static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
-
-struct RandomMiiValues {
- std::array<u8, 0xbc> values{};
-};
-static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
-
-struct RandomMiiData4 {
- Gender gender{};
- Age age{};
- Race race{};
- u32 values_count{};
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
-
-struct RandomMiiData3 {
- u32 arg_1;
- u32 arg_2;
- u32 values_count;
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
-
-struct RandomMiiData2 {
- u32 arg_1;
- u32 values_count;
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
-
-struct DefaultMii {
- u32 face_type{};
- u32 face_color{};
- u32 face_wrinkle{};
- u32 face_makeup{};
- u32 hair_type{};
- u32 hair_color{};
- u32 hair_flip{};
- u32 eye_type{};
- u32 eye_color{};
- u32 eye_scale{};
- u32 eye_aspect{};
- u32 eye_rotate{};
- u32 eye_x{};
- u32 eye_y{};
- u32 eyebrow_type{};
- u32 eyebrow_color{};
- u32 eyebrow_scale{};
- u32 eyebrow_aspect{};
- u32 eyebrow_rotate{};
- u32 eyebrow_x{};
- u32 eyebrow_y{};
- u32 nose_type{};
- u32 nose_scale{};
- u32 nose_y{};
- u32 mouth_type{};
- u32 mouth_color{};
- u32 mouth_scale{};
- u32 mouth_aspect{};
- u32 mouth_y{};
- u32 mustache_type{};
- u32 beard_type{};
- u32 beard_color{};
- u32 mustache_scale{};
- u32 mustache_y{};
- u32 glasses_type{};
- u32 glasses_color{};
- u32 glasses_scale{};
- u32 glasses_y{};
- u32 mole_type{};
- u32 mole_scale{};
- u32 mole_x{};
- u32 mole_y{};
- u32 height{};
- u32 weight{};
- Gender gender{};
- u32 favorite_color{};
- u32 region{};
- FontRegion font_region{};
- u32 type{};
- INSERT_PADDING_WORDS(5);
-};
-static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
-
-#pragma pack(pop)
-
-} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp
new file mode 100644
index 000000000..bb948c628
--- /dev/null
+++ b/src/core/hle/service/mii/types/char_info.cpp
@@ -0,0 +1,482 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/mii/types/char_info.h"
+#include "core/hle/service/mii/types/store_data.h"
+
+namespace Service::Mii {
+
+void CharInfo::SetFromStoreData(const StoreData& store_data) {
+ name = store_data.GetNickname();
+ null_terminator = '\0';
+ create_id = store_data.GetCreateId();
+ font_region = store_data.GetFontRegion();
+ favorite_color = store_data.GetFavoriteColor();
+ gender = store_data.GetGender();
+ height = store_data.GetHeight();
+ build = store_data.GetBuild();
+ type = store_data.GetType();
+ region_move = store_data.GetRegionMove();
+ faceline_type = store_data.GetFacelineType();
+ faceline_color = store_data.GetFacelineColor();
+ faceline_wrinkle = store_data.GetFacelineWrinkle();
+ faceline_make = store_data.GetFacelineMake();
+ hair_type = store_data.GetHairType();
+ hair_color = store_data.GetHairColor();
+ hair_flip = store_data.GetHairFlip();
+ eye_type = store_data.GetEyeType();
+ eye_color = store_data.GetEyeColor();
+ eye_scale = store_data.GetEyeScale();
+ eye_aspect = store_data.GetEyeAspect();
+ eye_rotate = store_data.GetEyeRotate();
+ eye_x = store_data.GetEyeX();
+ eye_y = store_data.GetEyeY();
+ eyebrow_type = store_data.GetEyebrowType();
+ eyebrow_color = store_data.GetEyebrowColor();
+ eyebrow_scale = store_data.GetEyebrowScale();
+ eyebrow_aspect = store_data.GetEyebrowAspect();
+ eyebrow_rotate = store_data.GetEyebrowRotate();
+ eyebrow_x = store_data.GetEyebrowX();
+ eyebrow_y = store_data.GetEyebrowY();
+ nose_type = store_data.GetNoseType();
+ nose_scale = store_data.GetNoseScale();
+ nose_y = store_data.GetNoseY();
+ mouth_type = store_data.GetMouthType();
+ mouth_color = store_data.GetMouthColor();
+ mouth_scale = store_data.GetMouthScale();
+ mouth_aspect = store_data.GetMouthAspect();
+ mouth_y = store_data.GetMouthY();
+ beard_color = store_data.GetBeardColor();
+ beard_type = store_data.GetBeardType();
+ mustache_type = store_data.GetMustacheType();
+ mustache_scale = store_data.GetMustacheScale();
+ mustache_y = store_data.GetMustacheY();
+ glass_type = store_data.GetGlassType();
+ glass_color = store_data.GetGlassColor();
+ glass_scale = store_data.GetGlassScale();
+ glass_y = store_data.GetGlassY();
+ mole_type = store_data.GetMoleType();
+ mole_scale = store_data.GetMoleScale();
+ mole_x = store_data.GetMoleX();
+ mole_y = store_data.GetMoleY();
+ padding = '\0';
+}
+
+ValidationResult CharInfo::Verify() const {
+ if (!create_id.IsValid()) {
+ return ValidationResult::InvalidCreateId;
+ }
+ if (!name.IsValid()) {
+ return ValidationResult::InvalidName;
+ }
+ if (font_region > FontRegion::Max) {
+ return ValidationResult::InvalidFont;
+ }
+ if (favorite_color > FavoriteColor::Max) {
+ return ValidationResult::InvalidColor;
+ }
+ if (gender > Gender::Max) {
+ return ValidationResult::InvalidGender;
+ }
+ if (height > MaxHeight) {
+ return ValidationResult::InvalidHeight;
+ }
+ if (build > MaxBuild) {
+ return ValidationResult::InvalidBuild;
+ }
+ if (type > MaxType) {
+ return ValidationResult::InvalidType;
+ }
+ if (region_move > MaxRegionMove) {
+ return ValidationResult::InvalidRegionMove;
+ }
+ if (faceline_type > FacelineType::Max) {
+ return ValidationResult::InvalidFacelineType;
+ }
+ if (faceline_color > FacelineColor::Max) {
+ return ValidationResult::InvalidFacelineColor;
+ }
+ if (faceline_wrinkle > FacelineWrinkle::Max) {
+ return ValidationResult::InvalidFacelineWrinkle;
+ }
+ if (faceline_make > FacelineMake::Max) {
+ return ValidationResult::InvalidFacelineMake;
+ }
+ if (hair_type > HairType::Max) {
+ return ValidationResult::InvalidHairType;
+ }
+ if (hair_color > CommonColor::Max) {
+ return ValidationResult::InvalidHairColor;
+ }
+ if (hair_flip > HairFlip::Max) {
+ return ValidationResult::InvalidHairFlip;
+ }
+ if (eye_type > EyeType::Max) {
+ return ValidationResult::InvalidEyeType;
+ }
+ if (eye_color > CommonColor::Max) {
+ return ValidationResult::InvalidEyeColor;
+ }
+ if (eye_scale > MaxEyeScale) {
+ return ValidationResult::InvalidEyeScale;
+ }
+ if (eye_aspect > MaxEyeAspect) {
+ return ValidationResult::InvalidEyeAspect;
+ }
+ if (eye_rotate > MaxEyeX) {
+ return ValidationResult::InvalidEyeRotate;
+ }
+ if (eye_x > MaxEyeX) {
+ return ValidationResult::InvalidEyeX;
+ }
+ if (eye_y > MaxEyeY) {
+ return ValidationResult::InvalidEyeY;
+ }
+ if (eyebrow_type > EyebrowType::Max) {
+ return ValidationResult::InvalidEyebrowType;
+ }
+ if (eyebrow_color > CommonColor::Max) {
+ return ValidationResult::InvalidEyebrowColor;
+ }
+ if (eyebrow_scale > MaxEyebrowScale) {
+ return ValidationResult::InvalidEyebrowScale;
+ }
+ if (eyebrow_aspect > MaxEyebrowAspect) {
+ return ValidationResult::InvalidEyebrowAspect;
+ }
+ if (eyebrow_rotate > MaxEyebrowRotate) {
+ return ValidationResult::InvalidEyebrowRotate;
+ }
+ if (eyebrow_x > MaxEyebrowX) {
+ return ValidationResult::InvalidEyebrowX;
+ }
+ if (eyebrow_y > MaxEyebrowY) {
+ return ValidationResult::InvalidEyebrowY;
+ }
+ if (nose_type > NoseType::Max) {
+ return ValidationResult::InvalidNoseType;
+ }
+ if (nose_scale > MaxNoseScale) {
+ return ValidationResult::InvalidNoseScale;
+ }
+ if (nose_y > MaxNoseY) {
+ return ValidationResult::InvalidNoseY;
+ }
+ if (mouth_type > MouthType::Max) {
+ return ValidationResult::InvalidMouthType;
+ }
+ if (mouth_color > CommonColor::Max) {
+ return ValidationResult::InvalidMouthColor;
+ }
+ if (mouth_scale > MaxMouthScale) {
+ return ValidationResult::InvalidMouthScale;
+ }
+ if (mouth_aspect > MaxMoutAspect) {
+ return ValidationResult::InvalidMouthAspect;
+ }
+ if (mouth_y > MaxMouthY) {
+ return ValidationResult::InvalidMoleY;
+ }
+ if (beard_color > CommonColor::Max) {
+ return ValidationResult::InvalidBeardColor;
+ }
+ if (beard_type > BeardType::Max) {
+ return ValidationResult::InvalidBeardType;
+ }
+ if (mustache_type > MustacheType::Max) {
+ return ValidationResult::InvalidMustacheType;
+ }
+ if (mustache_scale > MaxMustacheScale) {
+ return ValidationResult::InvalidMustacheScale;
+ }
+ if (mustache_y > MasMustacheY) {
+ return ValidationResult::InvalidMustacheY;
+ }
+ if (glass_type > GlassType::Max) {
+ return ValidationResult::InvalidGlassType;
+ }
+ if (glass_color > CommonColor::Max) {
+ return ValidationResult::InvalidGlassColor;
+ }
+ if (glass_scale > MaxGlassScale) {
+ return ValidationResult::InvalidGlassScale;
+ }
+ if (glass_y > MaxGlassY) {
+ return ValidationResult::InvalidGlassY;
+ }
+ if (mole_type > MoleType::Max) {
+ return ValidationResult::InvalidMoleType;
+ }
+ if (mole_scale > MaxMoleScale) {
+ return ValidationResult::InvalidMoleScale;
+ }
+ if (mole_x > MaxMoleX) {
+ return ValidationResult::InvalidMoleX;
+ }
+ if (mole_y > MaxMoleY) {
+ return ValidationResult::InvalidMoleY;
+ }
+ return ValidationResult::NoErrors;
+}
+
+Common::UUID CharInfo::GetCreateId() const {
+ return create_id;
+}
+
+Nickname CharInfo::GetNickname() const {
+ return name;
+}
+
+FontRegion CharInfo::GetFontRegion() const {
+ return font_region;
+}
+
+FavoriteColor CharInfo::GetFavoriteColor() const {
+ return favorite_color;
+}
+
+Gender CharInfo::GetGender() const {
+ return gender;
+}
+
+u8 CharInfo::GetHeight() const {
+ return height;
+}
+
+u8 CharInfo::GetBuild() const {
+ return build;
+}
+
+u8 CharInfo::GetType() const {
+ return type;
+}
+
+u8 CharInfo::GetRegionMove() const {
+ return region_move;
+}
+
+FacelineType CharInfo::GetFacelineType() const {
+ return faceline_type;
+}
+
+FacelineColor CharInfo::GetFacelineColor() const {
+ return faceline_color;
+}
+
+FacelineWrinkle CharInfo::GetFacelineWrinkle() const {
+ return faceline_wrinkle;
+}
+
+FacelineMake CharInfo::GetFacelineMake() const {
+ return faceline_make;
+}
+
+HairType CharInfo::GetHairType() const {
+ return hair_type;
+}
+
+CommonColor CharInfo::GetHairColor() const {
+ return hair_color;
+}
+
+HairFlip CharInfo::GetHairFlip() const {
+ return hair_flip;
+}
+
+EyeType CharInfo::GetEyeType() const {
+ return eye_type;
+}
+
+CommonColor CharInfo::GetEyeColor() const {
+ return eye_color;
+}
+
+u8 CharInfo::GetEyeScale() const {
+ return eye_scale;
+}
+
+u8 CharInfo::GetEyeAspect() const {
+ return eye_aspect;
+}
+
+u8 CharInfo::GetEyeRotate() const {
+ return eye_rotate;
+}
+
+u8 CharInfo::GetEyeX() const {
+ return eye_x;
+}
+
+u8 CharInfo::GetEyeY() const {
+ return eye_y;
+}
+
+EyebrowType CharInfo::GetEyebrowType() const {
+ return eyebrow_type;
+}
+
+CommonColor CharInfo::GetEyebrowColor() const {
+ return eyebrow_color;
+}
+
+u8 CharInfo::GetEyebrowScale() const {
+ return eyebrow_scale;
+}
+
+u8 CharInfo::GetEyebrowAspect() const {
+ return eyebrow_aspect;
+}
+
+u8 CharInfo::GetEyebrowRotate() const {
+ return eyebrow_rotate;
+}
+
+u8 CharInfo::GetEyebrowX() const {
+ return eyebrow_x;
+}
+
+u8 CharInfo::GetEyebrowY() const {
+ return eyebrow_y;
+}
+
+NoseType CharInfo::GetNoseType() const {
+ return nose_type;
+}
+
+u8 CharInfo::GetNoseScale() const {
+ return nose_scale;
+}
+
+u8 CharInfo::GetNoseY() const {
+ return nose_y;
+}
+
+MouthType CharInfo::GetMouthType() const {
+ return mouth_type;
+}
+
+CommonColor CharInfo::GetMouthColor() const {
+ return mouth_color;
+}
+
+u8 CharInfo::GetMouthScale() const {
+ return mouth_scale;
+}
+
+u8 CharInfo::GetMouthAspect() const {
+ return mouth_aspect;
+}
+
+u8 CharInfo::GetMouthY() const {
+ return mouth_y;
+}
+
+CommonColor CharInfo::GetBeardColor() const {
+ return beard_color;
+}
+
+BeardType CharInfo::GetBeardType() const {
+ return beard_type;
+}
+
+MustacheType CharInfo::GetMustacheType() const {
+ return mustache_type;
+}
+
+u8 CharInfo::GetMustacheScale() const {
+ return mustache_scale;
+}
+
+u8 CharInfo::GetMustacheY() const {
+ return mustache_y;
+}
+
+GlassType CharInfo::GetGlassType() const {
+ return glass_type;
+}
+
+CommonColor CharInfo::GetGlassColor() const {
+ return glass_color;
+}
+
+u8 CharInfo::GetGlassScale() const {
+ return glass_scale;
+}
+
+u8 CharInfo::GetGlassY() const {
+ return glass_y;
+}
+
+MoleType CharInfo::GetMoleType() const {
+ return mole_type;
+}
+
+u8 CharInfo::GetMoleScale() const {
+ return mole_scale;
+}
+
+u8 CharInfo::GetMoleX() const {
+ return mole_x;
+}
+
+u8 CharInfo::GetMoleY() const {
+ return mole_y;
+}
+
+bool CharInfo::operator==(const CharInfo& info) {
+ bool is_identical = info.Verify() == ValidationResult::NoErrors;
+ is_identical &= name.data == info.GetNickname().data;
+ is_identical &= create_id == info.GetCreateId();
+ is_identical &= font_region == info.GetFontRegion();
+ is_identical &= favorite_color == info.GetFavoriteColor();
+ is_identical &= gender == info.GetGender();
+ is_identical &= height == info.GetHeight();
+ is_identical &= build == info.GetBuild();
+ is_identical &= type == info.GetType();
+ is_identical &= region_move == info.GetRegionMove();
+ is_identical &= faceline_type == info.GetFacelineType();
+ is_identical &= faceline_color == info.GetFacelineColor();
+ is_identical &= faceline_wrinkle == info.GetFacelineWrinkle();
+ is_identical &= faceline_make == info.GetFacelineMake();
+ is_identical &= hair_type == info.GetHairType();
+ is_identical &= hair_color == info.GetHairColor();
+ is_identical &= hair_flip == info.GetHairFlip();
+ is_identical &= eye_type == info.GetEyeType();
+ is_identical &= eye_color == info.GetEyeColor();
+ is_identical &= eye_scale == info.GetEyeScale();
+ is_identical &= eye_aspect == info.GetEyeAspect();
+ is_identical &= eye_rotate == info.GetEyeRotate();
+ is_identical &= eye_x == info.GetEyeX();
+ is_identical &= eye_y == info.GetEyeY();
+ is_identical &= eyebrow_type == info.GetEyebrowType();
+ is_identical &= eyebrow_color == info.GetEyebrowColor();
+ is_identical &= eyebrow_scale == info.GetEyebrowScale();
+ is_identical &= eyebrow_aspect == info.GetEyebrowAspect();
+ is_identical &= eyebrow_rotate == info.GetEyebrowRotate();
+ is_identical &= eyebrow_x == info.GetEyebrowX();
+ is_identical &= eyebrow_y == info.GetEyebrowY();
+ is_identical &= nose_type == info.GetNoseType();
+ is_identical &= nose_scale == info.GetNoseScale();
+ is_identical &= nose_y == info.GetNoseY();
+ is_identical &= mouth_type == info.GetMouthType();
+ is_identical &= mouth_color == info.GetMouthColor();
+ is_identical &= mouth_scale == info.GetMouthScale();
+ is_identical &= mouth_aspect == info.GetMouthAspect();
+ is_identical &= mouth_y == info.GetMouthY();
+ is_identical &= beard_color == info.GetBeardColor();
+ is_identical &= beard_type == info.GetBeardType();
+ is_identical &= mustache_type == info.GetMustacheType();
+ is_identical &= mustache_scale == info.GetMustacheScale();
+ is_identical &= mustache_y == info.GetMustacheY();
+ is_identical &= glass_type == info.GetGlassType();
+ is_identical &= glass_color == info.GetGlassColor();
+ is_identical &= glass_scale == info.GetGlassScale();
+ is_identical &= glass_y == info.GetGlassY();
+ is_identical &= mole_type == info.GetMoleType();
+ is_identical &= mole_scale == info.GetMoleScale();
+ is_identical &= mole_x == info.GetMoleX();
+ is_identical &= mole_y == info.GetMoleY();
+ return is_identical;
+}
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h
new file mode 100644
index 000000000..d069b221f
--- /dev/null
+++ b/src/core/hle/service/mii/types/char_info.h
@@ -0,0 +1,137 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/mii/mii_types.h"
+
+namespace Service::Mii {
+class StoreData;
+
+// This is nn::mii::detail::CharInfoRaw
+class CharInfo {
+public:
+ void SetFromStoreData(const StoreData& store_data_raw);
+
+ ValidationResult Verify() const;
+
+ Common::UUID GetCreateId() const;
+ Nickname GetNickname() const;
+ FontRegion GetFontRegion() const;
+ FavoriteColor GetFavoriteColor() const;
+ Gender GetGender() const;
+ u8 GetHeight() const;
+ u8 GetBuild() const;
+ u8 GetType() const;
+ u8 GetRegionMove() const;
+ FacelineType GetFacelineType() const;
+ FacelineColor GetFacelineColor() const;
+ FacelineWrinkle GetFacelineWrinkle() const;
+ FacelineMake GetFacelineMake() const;
+ HairType GetHairType() const;
+ CommonColor GetHairColor() const;
+ HairFlip GetHairFlip() const;
+ EyeType GetEyeType() const;
+ CommonColor GetEyeColor() const;
+ u8 GetEyeScale() const;
+ u8 GetEyeAspect() const;
+ u8 GetEyeRotate() const;
+ u8 GetEyeX() const;
+ u8 GetEyeY() const;
+ EyebrowType GetEyebrowType() const;
+ CommonColor GetEyebrowColor() const;
+ u8 GetEyebrowScale() const;
+ u8 GetEyebrowAspect() const;
+ u8 GetEyebrowRotate() const;
+ u8 GetEyebrowX() const;
+ u8 GetEyebrowY() const;
+ NoseType GetNoseType() const;
+ u8 GetNoseScale() const;
+ u8 GetNoseY() const;
+ MouthType GetMouthType() const;
+ CommonColor GetMouthColor() const;
+ u8 GetMouthScale() const;
+ u8 GetMouthAspect() const;
+ u8 GetMouthY() const;
+ CommonColor GetBeardColor() const;
+ BeardType GetBeardType() const;
+ MustacheType GetMustacheType() const;
+ u8 GetMustacheScale() const;
+ u8 GetMustacheY() const;
+ GlassType GetGlassType() const;
+ CommonColor GetGlassColor() const;
+ u8 GetGlassScale() const;
+ u8 GetGlassY() const;
+ MoleType GetMoleType() const;
+ u8 GetMoleScale() const;
+ u8 GetMoleX() const;
+ u8 GetMoleY() const;
+
+ bool operator==(const CharInfo& info);
+
+private:
+ Common::UUID create_id;
+ Nickname name;
+ u16 null_terminator;
+ FontRegion font_region;
+ FavoriteColor favorite_color;
+ Gender gender;
+ u8 height;
+ u8 build;
+ u8 type;
+ u8 region_move;
+ FacelineType faceline_type;
+ FacelineColor faceline_color;
+ FacelineWrinkle faceline_wrinkle;
+ FacelineMake faceline_make;
+ HairType hair_type;
+ CommonColor hair_color;
+ HairFlip hair_flip;
+ EyeType eye_type;
+ CommonColor eye_color;
+ u8 eye_scale;
+ u8 eye_aspect;
+ u8 eye_rotate;
+ u8 eye_x;
+ u8 eye_y;
+ EyebrowType eyebrow_type;
+ CommonColor eyebrow_color;
+ u8 eyebrow_scale;
+ u8 eyebrow_aspect;
+ u8 eyebrow_rotate;
+ u8 eyebrow_x;
+ u8 eyebrow_y;
+ NoseType nose_type;
+ u8 nose_scale;
+ u8 nose_y;
+ MouthType mouth_type;
+ CommonColor mouth_color;
+ u8 mouth_scale;
+ u8 mouth_aspect;
+ u8 mouth_y;
+ CommonColor beard_color;
+ BeardType beard_type;
+ MustacheType mustache_type;
+ u8 mustache_scale;
+ u8 mustache_y;
+ GlassType glass_type;
+ CommonColor glass_color;
+ u8 glass_scale;
+ u8 glass_y;
+ MoleType mole_type;
+ u8 mole_scale;
+ u8 mole_x;
+ u8 mole_y;
+ u8 padding;
+};
+static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
+static_assert(std::has_unique_object_representations_v<CharInfo>,
+ "All bits of CharInfo must contribute to its value.");
+
+struct CharInfoElement {
+ CharInfo char_info{};
+ Source source{};
+};
+static_assert(sizeof(CharInfoElement) == 0x5c, "CharInfoElement has incorrect size.");
+
+}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp
new file mode 100644
index 000000000..659288b51
--- /dev/null
+++ b/src/core/hle/service/mii/types/core_data.cpp
@@ -0,0 +1,601 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/assert.h"
+#include "core/hle/service/mii/mii_util.h"
+#include "core/hle/service/mii/types/core_data.h"
+#include "core/hle/service/mii/types/raw_data.h"
+
+namespace Service::Mii {
+
+void CoreData::SetDefault() {
+ data = {};
+ name = GetDefaultNickname();
+}
+
+void CoreData::BuildRandom(Age age, Gender gender, Race race) {
+ if (gender == Gender::All) {
+ gender = MiiUtil::GetRandomValue(Gender::Max);
+ }
+
+ if (age == Age::All) {
+ const auto random{MiiUtil::GetRandomValue<int>(10)};
+ if (random >= 8) {
+ age = Age::Old;
+ } else if (random >= 4) {
+ age = Age::Normal;
+ } else {
+ age = Age::Young;
+ }
+ }
+
+ if (race == Race::All) {
+ const auto random{MiiUtil::GetRandomValue<int>(10)};
+ if (random >= 8) {
+ race = Race::Black;
+ } else if (random >= 4) {
+ race = Race::White;
+ } else {
+ race = Race::Asian;
+ }
+ }
+
+ SetGender(gender);
+ SetFavoriteColor(MiiUtil::GetRandomValue(FavoriteColor::Max));
+ SetRegionMove(0);
+ SetFontRegion(FontRegion::Standard);
+ SetType(0);
+ SetHeight(64);
+ SetBuild(64);
+
+ u32 axis_y{};
+ if (gender == Gender::Female && age == Age::Young) {
+ axis_y = MiiUtil::GetRandomValue<u32>(3);
+ }
+
+ const std::size_t index{3 * static_cast<std::size_t>(age) +
+ 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
+
+ const auto& faceline_type_info{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{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))};
+
+ data.faceline_type.Assign(
+ faceline_type_info
+ .values[MiiUtil::GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
+ data.faceline_color.Assign(
+ faceline_color_info
+ .values[MiiUtil::GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
+ data.faceline_wrinkle.Assign(
+ faceline_wrinkle_info
+ .values[MiiUtil::GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
+ data.faceline_makeup.Assign(
+ faceline_makeup_info
+ .values[MiiUtil::GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
+
+ data.hair_type.Assign(
+ hair_type_info.values[MiiUtil::GetRandomValue<std::size_t>(hair_type_info.values_count)]);
+ SetHairColor(RawData::GetHairColorFromVer3(
+ hair_color_info
+ .values[MiiUtil::GetRandomValue<std::size_t>(hair_color_info.values_count)]));
+ SetHairFlip(MiiUtil::GetRandomValue(HairFlip::Max));
+
+ data.eye_type.Assign(
+ eye_type_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_type_info.values_count)]);
+
+ const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
+ const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
+ const auto eye_rotate_offset{32 - RawData::EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
+ const auto eye_rotate{32 - RawData::EyeRotateLookup[data.eye_type]};
+
+ SetEyeColor(RawData::GetEyeColorFromVer3(
+ eye_color_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_color_info.values_count)]));
+ SetEyeScale(4);
+ SetEyeAspect(3);
+ SetEyeRotate(static_cast<u8>(eye_rotate_offset - eye_rotate));
+ SetEyeX(2);
+ SetEyeY(static_cast<u8>(axis_y + 12));
+
+ data.eyebrow_type.Assign(
+ eyebrow_type_info
+ .values[MiiUtil::GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
+
+ const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
+ const auto eyebrow_y{race == Race::Asian ? 9 : 10};
+ const auto eyebrow_rotate_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6};
+ const auto eyebrow_rotate{
+ 32 - RawData::EyebrowRotateLookup[static_cast<std::size_t>(data.eyebrow_type.Value())]};
+
+ SetEyebrowColor(GetHairColor());
+ SetEyebrowScale(4);
+ SetEyebrowAspect(3);
+ SetEyebrowRotate(static_cast<u8>(eyebrow_rotate_offset - eyebrow_rotate));
+ SetEyebrowX(2);
+ SetEyebrowY(static_cast<u8>(axis_y + eyebrow_y));
+
+ data.nose_type.Assign(
+ nose_type_info.values[MiiUtil::GetRandomValue<std::size_t>(nose_type_info.values_count)]);
+ SetNoseScale(gender == Gender::Female ? 3 : 4);
+ SetNoseY(static_cast<u8>(axis_y + 9));
+
+ const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue<int>(4) : 0};
+
+ data.mouth_type.Assign(
+ mouth_type_info.values[MiiUtil::GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
+ SetMouthColor(RawData::GetMouthColorFromVer3(mouth_color));
+ SetMouthScale(4);
+ SetMouthAspect(3);
+ SetMouthY(static_cast<u8>(axis_y + 13));
+
+ SetBeardColor(GetHairColor());
+ SetMustacheScale(4);
+
+ if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue<int>(10) < 2) {
+ const auto mustache_and_beard_flag{MiiUtil::GetRandomValue(BeardAndMustacheFlag::All)};
+
+ auto beard_type{BeardType::None};
+ auto mustache_type{MustacheType::None};
+
+ if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
+ BeardAndMustacheFlag::Beard) {
+ beard_type = MiiUtil::GetRandomValue(BeardType::Min, BeardType::Max);
+ }
+
+ if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
+ BeardAndMustacheFlag::Mustache) {
+ mustache_type = MiiUtil::GetRandomValue(MustacheType::Min, MustacheType::Max);
+ }
+
+ SetMustacheType(mustache_type);
+ SetBeardType(beard_type);
+ SetMustacheY(10);
+ } else {
+ SetMustacheType(MustacheType::None);
+ SetBeardType(BeardType::None);
+ SetMustacheY(static_cast<u8>(axis_y + 10));
+ }
+
+ const auto glasses_type_start{MiiUtil::GetRandomValue<std::size_t>(100)};
+ u8 glasses_type{};
+ while (glasses_type_start < glasses_type_info.values[glasses_type]) {
+ if (++glasses_type >= glasses_type_info.values_count) {
+ ASSERT(false);
+ break;
+ }
+ }
+
+ SetGlassType(static_cast<GlassType>(glasses_type));
+ SetGlassColor(RawData::GetGlassColorFromVer3(0));
+ SetGlassScale(4);
+
+ SetMoleType(MoleType::None);
+ SetMoleScale(4);
+ SetMoleX(2);
+ SetMoleY(20);
+}
+
+u32 CoreData::IsValid() const {
+ // TODO: Complete this
+ return 0;
+}
+
+void CoreData::SetFontRegion(FontRegion value) {
+ data.font_region.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetFavoriteColor(FavoriteColor value) {
+ data.favorite_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetGender(Gender value) {
+ data.gender.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetHeight(u8 value) {
+ data.height.Assign(value);
+}
+
+void CoreData::SetBuild(u8 value) {
+ data.build.Assign(value);
+}
+
+void CoreData::SetType(u8 value) {
+ data.type.Assign(value);
+}
+
+void CoreData::SetRegionMove(u8 value) {
+ data.region_move.Assign(value);
+}
+
+void CoreData::SetFacelineType(FacelineType value) {
+ data.faceline_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetFacelineColor(FacelineColor value) {
+ data.faceline_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetFacelineWrinkle(FacelineWrinkle value) {
+ data.faceline_wrinkle.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetFacelineMake(FacelineMake value) {
+ data.faceline_makeup.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetHairType(HairType value) {
+ data.hair_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetHairColor(CommonColor value) {
+ data.hair_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetHairFlip(HairFlip value) {
+ data.hair_flip.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetEyeType(EyeType value) {
+ data.eye_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetEyeColor(CommonColor value) {
+ data.eye_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetEyeScale(u8 value) {
+ data.eye_scale.Assign(value);
+}
+
+void CoreData::SetEyeAspect(u8 value) {
+ data.eye_aspect.Assign(value);
+}
+
+void CoreData::SetEyeRotate(u8 value) {
+ data.eye_rotate.Assign(value);
+}
+
+void CoreData::SetEyeX(u8 value) {
+ data.eye_x.Assign(value);
+}
+
+void CoreData::SetEyeY(u8 value) {
+ data.eye_y.Assign(value);
+}
+
+void CoreData::SetEyebrowType(EyebrowType value) {
+ data.eyebrow_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetEyebrowColor(CommonColor value) {
+ data.eyebrow_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetEyebrowScale(u8 value) {
+ data.eyebrow_scale.Assign(value);
+}
+
+void CoreData::SetEyebrowAspect(u8 value) {
+ data.eyebrow_aspect.Assign(value);
+}
+
+void CoreData::SetEyebrowRotate(u8 value) {
+ data.eyebrow_rotate.Assign(value);
+}
+
+void CoreData::SetEyebrowX(u8 value) {
+ data.eyebrow_x.Assign(value);
+}
+
+void CoreData::SetEyebrowY(u8 value) {
+ data.eyebrow_y.Assign(value);
+}
+
+void CoreData::SetNoseType(NoseType value) {
+ data.nose_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetNoseScale(u8 value) {
+ data.nose_scale.Assign(value);
+}
+
+void CoreData::SetNoseY(u8 value) {
+ data.nose_y.Assign(value);
+}
+
+void CoreData::SetMouthType(u8 value) {
+ data.mouth_type.Assign(value);
+}
+
+void CoreData::SetMouthColor(CommonColor value) {
+ data.mouth_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetMouthScale(u8 value) {
+ data.mouth_scale.Assign(value);
+}
+
+void CoreData::SetMouthAspect(u8 value) {
+ data.mouth_aspect.Assign(value);
+}
+
+void CoreData::SetMouthY(u8 value) {
+ data.mouth_y.Assign(value);
+}
+
+void CoreData::SetBeardColor(CommonColor value) {
+ data.beard_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetBeardType(BeardType value) {
+ data.beard_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetMustacheType(MustacheType value) {
+ data.mustache_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetMustacheScale(u8 value) {
+ data.mustache_scale.Assign(value);
+}
+
+void CoreData::SetMustacheY(u8 value) {
+ data.mustache_y.Assign(value);
+}
+
+void CoreData::SetGlassType(GlassType value) {
+ data.glasses_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetGlassColor(CommonColor value) {
+ data.glasses_color.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetGlassScale(u8 value) {
+ data.glasses_scale.Assign(value);
+}
+
+void CoreData::SetGlassY(u8 value) {
+ data.glasses_y.Assign(value);
+}
+
+void CoreData::SetMoleType(MoleType value) {
+ data.mole_type.Assign(static_cast<u32>(value));
+}
+
+void CoreData::SetMoleScale(u8 value) {
+ data.mole_scale.Assign(value);
+}
+
+void CoreData::SetMoleX(u8 value) {
+ data.mole_x.Assign(value);
+}
+
+void CoreData::SetMoleY(u8 value) {
+ data.mole_y.Assign(value);
+}
+
+void CoreData::SetNickname(Nickname nickname) {
+ name = nickname;
+}
+
+FontRegion CoreData::GetFontRegion() const {
+ return static_cast<FontRegion>(data.font_region.Value());
+}
+
+FavoriteColor CoreData::GetFavoriteColor() const {
+ return static_cast<FavoriteColor>(data.favorite_color.Value());
+}
+
+Gender CoreData::GetGender() const {
+ return static_cast<Gender>(data.gender.Value());
+}
+
+u8 CoreData::GetHeight() const {
+ return static_cast<u8>(data.height.Value());
+}
+
+u8 CoreData::GetBuild() const {
+ return static_cast<u8>(data.build.Value());
+}
+
+u8 CoreData::GetType() const {
+ return static_cast<u8>(data.type.Value());
+}
+
+u8 CoreData::GetRegionMove() const {
+ return static_cast<u8>(data.region_move.Value());
+}
+
+FacelineType CoreData::GetFacelineType() const {
+ return static_cast<FacelineType>(data.faceline_type.Value());
+}
+
+FacelineColor CoreData::GetFacelineColor() const {
+ return static_cast<FacelineColor>(data.faceline_color.Value());
+}
+
+FacelineWrinkle CoreData::GetFacelineWrinkle() const {
+ return static_cast<FacelineWrinkle>(data.faceline_wrinkle.Value());
+}
+
+FacelineMake CoreData::GetFacelineMake() const {
+ return static_cast<FacelineMake>(data.faceline_makeup.Value());
+}
+
+HairType CoreData::GetHairType() const {
+ return static_cast<HairType>(data.hair_type.Value());
+}
+
+CommonColor CoreData::GetHairColor() const {
+ return static_cast<CommonColor>(data.hair_color.Value());
+}
+
+HairFlip CoreData::GetHairFlip() const {
+ return static_cast<HairFlip>(data.hair_flip.Value());
+}
+
+EyeType CoreData::GetEyeType() const {
+ return static_cast<EyeType>(data.eye_type.Value());
+}
+
+CommonColor CoreData::GetEyeColor() const {
+ return static_cast<CommonColor>(data.eye_color.Value());
+}
+
+u8 CoreData::GetEyeScale() const {
+ return static_cast<u8>(data.eye_scale.Value());
+}
+
+u8 CoreData::GetEyeAspect() const {
+ return static_cast<u8>(data.eye_aspect.Value());
+}
+
+u8 CoreData::GetEyeRotate() const {
+ return static_cast<u8>(data.eye_rotate.Value());
+}
+
+u8 CoreData::GetEyeX() const {
+ return static_cast<u8>(data.eye_x.Value());
+}
+
+u8 CoreData::GetEyeY() const {
+ return static_cast<u8>(data.eye_y.Value());
+}
+
+EyebrowType CoreData::GetEyebrowType() const {
+ return static_cast<EyebrowType>(data.eyebrow_type.Value());
+}
+
+CommonColor CoreData::GetEyebrowColor() const {
+ return static_cast<CommonColor>(data.eyebrow_color.Value());
+}
+
+u8 CoreData::GetEyebrowScale() const {
+ return static_cast<u8>(data.eyebrow_scale.Value());
+}
+
+u8 CoreData::GetEyebrowAspect() const {
+ return static_cast<u8>(data.eyebrow_aspect.Value());
+}
+
+u8 CoreData::GetEyebrowRotate() const {
+ return static_cast<u8>(data.eyebrow_rotate.Value());
+}
+
+u8 CoreData::GetEyebrowX() const {
+ return static_cast<u8>(data.eyebrow_x.Value());
+}
+
+u8 CoreData::GetEyebrowY() const {
+ return static_cast<u8>(data.eyebrow_y.Value());
+}
+
+NoseType CoreData::GetNoseType() const {
+ return static_cast<NoseType>(data.nose_type.Value());
+}
+
+u8 CoreData::GetNoseScale() const {
+ return static_cast<u8>(data.nose_scale.Value());
+}
+
+u8 CoreData::GetNoseY() const {
+ return static_cast<u8>(data.nose_y.Value());
+}
+
+MouthType CoreData::GetMouthType() const {
+ return static_cast<MouthType>(data.mouth_type.Value());
+}
+
+CommonColor CoreData::GetMouthColor() const {
+ return static_cast<CommonColor>(data.mouth_color.Value());
+}
+
+u8 CoreData::GetMouthScale() const {
+ return static_cast<u8>(data.mouth_scale.Value());
+}
+
+u8 CoreData::GetMouthAspect() const {
+ return static_cast<u8>(data.mouth_aspect.Value());
+}
+
+u8 CoreData::GetMouthY() const {
+ return static_cast<u8>(data.mouth_y.Value());
+}
+
+CommonColor CoreData::GetBeardColor() const {
+ return static_cast<CommonColor>(data.beard_color.Value());
+}
+
+BeardType CoreData::GetBeardType() const {
+ return static_cast<BeardType>(data.beard_type.Value());
+}
+
+MustacheType CoreData::GetMustacheType() const {
+ return static_cast<MustacheType>(data.mustache_type.Value());
+}
+
+u8 CoreData::GetMustacheScale() const {
+ return static_cast<u8>(data.mustache_scale.Value());
+}
+
+u8 CoreData::GetMustacheY() const {
+ return static_cast<u8>(data.mustache_y.Value());
+}
+
+GlassType CoreData::GetGlassType() const {
+ return static_cast<GlassType>(data.glasses_type.Value());
+}
+
+CommonColor CoreData::GetGlassColor() const {
+ return static_cast<CommonColor>(data.glasses_color.Value());
+}
+
+u8 CoreData::GetGlassScale() const {
+ return static_cast<u8>(data.glasses_scale.Value());
+}
+
+u8 CoreData::GetGlassY() const {
+ return static_cast<u8>(data.glasses_y.Value());
+}
+
+MoleType CoreData::GetMoleType() const {
+ return static_cast<MoleType>(data.mole_type.Value());
+}
+
+u8 CoreData::GetMoleScale() const {
+ return static_cast<u8>(data.mole_scale.Value());
+}
+
+u8 CoreData::GetMoleX() const {
+ return static_cast<u8>(data.mole_x.Value());
+}
+
+u8 CoreData::GetMoleY() const {
+ return static_cast<u8>(data.mole_y.Value());
+}
+
+Nickname CoreData::GetNickname() const {
+ return name;
+}
+
+Nickname CoreData::GetDefaultNickname() const {
+ return {u'n', u'o', u' ', u'n', u'a', u'm', u'e'};
+}
+
+Nickname CoreData::GetInvalidNickname() const {
+ return {u'?', u'?', u'?'};
+}
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/core_data.h b/src/core/hle/service/mii/types/core_data.h
new file mode 100644
index 000000000..cebcd2ee4
--- /dev/null
+++ b/src/core/hle/service/mii/types/core_data.h
@@ -0,0 +1,216 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/mii/mii_types.h"
+
+namespace Service::Mii {
+
+struct StoreDataBitFields {
+ union {
+ u32 word_0{};
+
+ BitField<0, 8, u32> hair_type;
+ BitField<8, 7, u32> height;
+ BitField<15, 1, u32> mole_type;
+ BitField<16, 7, u32> build;
+ BitField<23, 1, u32> hair_flip;
+ BitField<24, 7, u32> hair_color;
+ BitField<31, 1, u32> type;
+ };
+
+ union {
+ u32 word_1{};
+
+ BitField<0, 7, u32> eye_color;
+ BitField<7, 1, u32> gender;
+ BitField<8, 7, u32> eyebrow_color;
+ BitField<16, 7, u32> mouth_color;
+ BitField<24, 7, u32> beard_color;
+ };
+
+ union {
+ u32 word_2{};
+
+ BitField<0, 7, u32> glasses_color;
+ BitField<8, 6, u32> eye_type;
+ BitField<14, 2, u32> region_move;
+ BitField<16, 6, u32> mouth_type;
+ BitField<22, 2, u32> font_region;
+ BitField<24, 5, u32> eye_y;
+ BitField<29, 3, u32> glasses_scale;
+ };
+
+ union {
+ u32 word_3{};
+
+ BitField<0, 5, u32> eyebrow_type;
+ BitField<5, 3, u32> mustache_type;
+ BitField<8, 5, u32> nose_type;
+ BitField<13, 3, u32> beard_type;
+ BitField<16, 5, u32> nose_y;
+ BitField<21, 3, u32> mouth_aspect;
+ BitField<24, 5, u32> mouth_y;
+ BitField<29, 3, u32> eyebrow_aspect;
+ };
+
+ union {
+ u32 word_4{};
+
+ BitField<0, 5, u32> mustache_y;
+ BitField<5, 3, u32> eye_rotate;
+ BitField<8, 5, u32> glasses_y;
+ BitField<13, 3, u32> eye_aspect;
+ BitField<16, 5, u32> mole_x;
+ BitField<21, 3, u32> eye_scale;
+ BitField<24, 5, u32> mole_y;
+ };
+
+ union {
+ u32 word_5{};
+
+ BitField<0, 5, u32> glasses_type;
+ BitField<8, 4, u32> favorite_color;
+ BitField<12, 4, u32> faceline_type;
+ BitField<16, 4, u32> faceline_color;
+ BitField<20, 4, u32> faceline_wrinkle;
+ BitField<24, 4, u32> faceline_makeup;
+ BitField<28, 4, u32> eye_x;
+ };
+
+ union {
+ u32 word_6{};
+
+ BitField<0, 4, u32> eyebrow_scale;
+ BitField<4, 4, u32> eyebrow_rotate;
+ BitField<8, 4, u32> eyebrow_x;
+ BitField<12, 4, u32> eyebrow_y;
+ BitField<16, 4, u32> nose_scale;
+ BitField<20, 4, u32> mouth_scale;
+ BitField<24, 4, u32> mustache_scale;
+ BitField<28, 4, u32> mole_scale;
+ };
+};
+static_assert(sizeof(StoreDataBitFields) == 0x1c, "StoreDataBitFields has incorrect size.");
+static_assert(std::is_trivially_copyable_v<StoreDataBitFields>,
+ "StoreDataBitFields is not trivially copyable.");
+
+class CoreData {
+public:
+ void SetDefault();
+ void BuildRandom(Age age, Gender gender, Race race);
+
+ u32 IsValid() const;
+
+ void SetFontRegion(FontRegion value);
+ void SetFavoriteColor(FavoriteColor value);
+ void SetGender(Gender value);
+ void SetHeight(u8 value);
+ void SetBuild(u8 value);
+ void SetType(u8 value);
+ void SetRegionMove(u8 value);
+ void SetFacelineType(FacelineType value);
+ void SetFacelineColor(FacelineColor value);
+ void SetFacelineWrinkle(FacelineWrinkle value);
+ void SetFacelineMake(FacelineMake value);
+ void SetHairType(HairType value);
+ void SetHairColor(CommonColor value);
+ void SetHairFlip(HairFlip value);
+ void SetEyeType(EyeType value);
+ void SetEyeColor(CommonColor value);
+ void SetEyeScale(u8 value);
+ void SetEyeAspect(u8 value);
+ void SetEyeRotate(u8 value);
+ void SetEyeX(u8 value);
+ void SetEyeY(u8 value);
+ void SetEyebrowType(EyebrowType value);
+ void SetEyebrowColor(CommonColor value);
+ void SetEyebrowScale(u8 value);
+ void SetEyebrowAspect(u8 value);
+ void SetEyebrowRotate(u8 value);
+ void SetEyebrowX(u8 value);
+ void SetEyebrowY(u8 value);
+ void SetNoseType(NoseType value);
+ void SetNoseScale(u8 value);
+ void SetNoseY(u8 value);
+ void SetMouthType(u8 value);
+ void SetMouthColor(CommonColor value);
+ void SetMouthScale(u8 value);
+ void SetMouthAspect(u8 value);
+ void SetMouthY(u8 value);
+ void SetBeardColor(CommonColor value);
+ void SetBeardType(BeardType value);
+ void SetMustacheType(MustacheType value);
+ void SetMustacheScale(u8 value);
+ void SetMustacheY(u8 value);
+ void SetGlassType(GlassType value);
+ void SetGlassColor(CommonColor value);
+ void SetGlassScale(u8 value);
+ void SetGlassY(u8 value);
+ void SetMoleType(MoleType value);
+ void SetMoleScale(u8 value);
+ void SetMoleX(u8 value);
+ void SetMoleY(u8 value);
+ void SetNickname(Nickname nickname);
+
+ FontRegion GetFontRegion() const;
+ FavoriteColor GetFavoriteColor() const;
+ Gender GetGender() const;
+ u8 GetHeight() const;
+ u8 GetBuild() const;
+ u8 GetType() const;
+ u8 GetRegionMove() const;
+ FacelineType GetFacelineType() const;
+ FacelineColor GetFacelineColor() const;
+ FacelineWrinkle GetFacelineWrinkle() const;
+ FacelineMake GetFacelineMake() const;
+ HairType GetHairType() const;
+ CommonColor GetHairColor() const;
+ HairFlip GetHairFlip() const;
+ EyeType GetEyeType() const;
+ CommonColor GetEyeColor() const;
+ u8 GetEyeScale() const;
+ u8 GetEyeAspect() const;
+ u8 GetEyeRotate() const;
+ u8 GetEyeX() const;
+ u8 GetEyeY() const;
+ EyebrowType GetEyebrowType() const;
+ CommonColor GetEyebrowColor() const;
+ u8 GetEyebrowScale() const;
+ u8 GetEyebrowAspect() const;
+ u8 GetEyebrowRotate() const;
+ u8 GetEyebrowX() const;
+ u8 GetEyebrowY() const;
+ NoseType GetNoseType() const;
+ u8 GetNoseScale() const;
+ u8 GetNoseY() const;
+ MouthType GetMouthType() const;
+ CommonColor GetMouthColor() const;
+ u8 GetMouthScale() const;
+ u8 GetMouthAspect() const;
+ u8 GetMouthY() const;
+ CommonColor GetBeardColor() const;
+ BeardType GetBeardType() const;
+ MustacheType GetMustacheType() const;
+ u8 GetMustacheScale() const;
+ u8 GetMustacheY() const;
+ GlassType GetGlassType() const;
+ CommonColor GetGlassColor() const;
+ u8 GetGlassScale() const;
+ u8 GetGlassY() const;
+ MoleType GetMoleType() const;
+ u8 GetMoleScale() const;
+ u8 GetMoleX() const;
+ u8 GetMoleY() const;
+ Nickname GetNickname() const;
+ Nickname GetDefaultNickname() const;
+ Nickname GetInvalidNickname() const;
+
+private:
+ StoreDataBitFields data{};
+ Nickname name{};
+};
+static_assert(sizeof(CoreData) == 0x30, "CoreData has incorrect size.");
+
+}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/types/raw_data.cpp
index e5245b791..5143abcc8 100644
--- a/src/core/hle/service/mii/raw_data.cpp
+++ b/src/core/hle/service/mii/types/raw_data.cpp
@@ -1,10 +1,87 @@
// SPDX-FileCopyrightText: Ryujinx Team and Contributors
// SPDX-License-Identifier: MIT
-#include "core/hle/service/mii/raw_data.h"
+#include "core/hle/service/mii/types/raw_data.h"
namespace Service::Mii::RawData {
+constexpr std::array<u8, static_cast<u8>(FacelineColor::Count)> FromVer3FacelineColorTable{
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5,
+};
+
+constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3HairColorTable{
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0,
+ 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
+ 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4,
+ 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5,
+ 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7,
+ 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4,
+};
+
+constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3EyeColorTable{
+ 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4,
+ 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
+ 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4,
+ 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
+ 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2,
+ 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+};
+
+constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3MouthlineColorTable{
+ 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
+ 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
+ 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
+ 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
+ 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3,
+ 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3,
+};
+
+constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3GlassColorTable{
+ 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3,
+ 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
+ 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
+ 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5,
+ 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4,
+ 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
+};
+
+constexpr std::array<u8, static_cast<u8>(GlassType::Count)> FromVer3GlassTypeTable{
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1,
+ 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7,
+};
+
+constexpr std::array<u8, 8> Ver3FacelineColorTable{
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
+};
+
+constexpr std::array<u8, 8> Ver3HairColorTable{
+ 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
+};
+
+constexpr std::array<u8, 6> Ver3EyeColorTable{
+ 0x8, 0x9, 0xa, 0xb, 0xc, 0xd,
+};
+
+constexpr std::array<u8, 5> Ver3MouthColorTable{
+ 0x13, 0x14, 0x15, 0x16, 0x17,
+};
+
+constexpr std::array<u8, 7> Ver3GlassColorTable{
+ 0x8, 0xe, 0xf, 0x10, 0x11, 0x12, 0x0,
+};
+
+const std::array<u8, 62> EyeRotateLookup{
+ 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
+ 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
+ 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
+};
+
+const std::array<u8, 24> EyebrowRotateLookup{
+ 0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x06, 0x08,
+ 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05,
+};
+
const std::array<Service::Mii::DefaultMii, 2> BaseMii{
Service::Mii::DefaultMii{
.face_type = 0,
@@ -51,11 +128,12 @@ const std::array<Service::Mii::DefaultMii, 2> BaseMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Male,
+ .gender = 0,
.favorite_color = 0,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
Service::Mii::DefaultMii{
.face_type = 0,
@@ -102,11 +180,12 @@ const std::array<Service::Mii::DefaultMii, 2> BaseMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Female,
+ .gender = 1,
.favorite_color = 0,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
};
@@ -156,11 +235,12 @@ const std::array<Service::Mii::DefaultMii, 6> DefaultMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Male,
+ .gender = 0,
.favorite_color = 4,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
Service::Mii::DefaultMii{
.face_type = 0,
@@ -207,11 +287,12 @@ const std::array<Service::Mii::DefaultMii, 6> DefaultMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Male,
+ .gender = 0,
.favorite_color = 5,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
Service::Mii::DefaultMii{
.face_type = 0,
@@ -258,11 +339,12 @@ const std::array<Service::Mii::DefaultMii, 6> DefaultMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Male,
+ .gender = 0,
.favorite_color = 0,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
Service::Mii::DefaultMii{
.face_type = 0,
@@ -309,11 +391,12 @@ const std::array<Service::Mii::DefaultMii, 6> DefaultMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Female,
+ .gender = 1,
.favorite_color = 2,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
Service::Mii::DefaultMii{
.face_type = 0,
@@ -360,11 +443,12 @@ const std::array<Service::Mii::DefaultMii, 6> DefaultMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Female,
+ .gender = 1,
.favorite_color = 6,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
Service::Mii::DefaultMii{
.face_type = 0,
@@ -411,176 +495,177 @@ const std::array<Service::Mii::DefaultMii, 6> DefaultMii{
.mole_y = 20,
.height = 64,
.weight = 64,
- .gender = Gender::Female,
+ .gender = 1,
.favorite_color = 7,
- .region = 0,
- .font_region = FontRegion::Standard,
+ .region_move = 0,
+ .font_region = 0,
.type = 0,
+ .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
},
};
-const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiFaceline{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(Race::Asian),
.values_count = 12,
.values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
},
};
-const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{
- Service::Mii::RandomMiiData3{
+const std::array<RandomMiiData3, 6> RandomMiiFacelineColor{
+ RandomMiiData3{
.arg_1 = 0,
.arg_2 = 0,
.values_count = 10,
.values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5},
},
- Service::Mii::RandomMiiData3{
+ RandomMiiData3{
.arg_1 = 0,
.arg_2 = 1,
.values_count = 10,
.values = {0, 0, 0, 0, 1, 1, 2, 3, 3, 3},
},
- Service::Mii::RandomMiiData3{
+ RandomMiiData3{
.arg_1 = 0,
.arg_2 = 2,
.values_count = 10,
.values = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2},
},
- Service::Mii::RandomMiiData3{
+ RandomMiiData3{
.arg_1 = 1,
.arg_2 = 0,
.values_count = 10,
.values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5},
},
- Service::Mii::RandomMiiData3{
+ RandomMiiData3{
.arg_1 = 1,
.arg_2 = 1,
.values_count = 10,
.values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 3},
},
- Service::Mii::RandomMiiData3{
+ RandomMiiData3{
.arg_1 = 1,
.arg_2 = 2,
.values_count = 10,
@@ -588,407 +673,407 @@ const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{
},
};
-const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiHairType{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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},
@@ -996,55 +1081,55 @@ const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{
};
const std::array<RandomMiiData3, 9> RandomMiiHairColor{
- Service::Mii::RandomMiiData3{
+ 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{
+ 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{
+ 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{
+ 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{
+ 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{
+ 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{
+ 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{
+ 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{
+ RandomMiiData3{
.arg_1 = 2,
.arg_2 = 2,
.values_count = 20,
@@ -1052,598 +1137,642 @@ const std::array<RandomMiiData3, 9> RandomMiiHairColor{
},
};
-const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiEyeType{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor{
- Service::Mii::RandomMiiData2{
+const std::array<RandomMiiData2, 3> RandomMiiEyeColor{
+ RandomMiiData2{
.arg_1 = 0,
.values_count = 10,
.values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
},
- Service::Mii::RandomMiiData2{
+ RandomMiiData2{
.arg_1 = 1,
.values_count = 10,
.values = {0, 1, 1, 2, 3, 3, 4, 4, 4, 5},
},
- Service::Mii::RandomMiiData2{
+ RandomMiiData2{
.arg_1 = 2,
.values_count = 10,
.values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
},
};
-const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiEyebrowType{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(Race::Asian),
.values_count = 9,
.values = {0, 3, 7, 8, 9, 10, 11, 13, 15},
},
};
-const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiNoseType{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(Race::Asian),
.values_count = 10,
.values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14},
},
};
-const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType{
- Service::Mii::RandomMiiData4{
- .gender = Gender::Male,
- .age = Age::Young,
- .race = Race::Black,
+const std::array<RandomMiiData4, 18> RandomMiiMouthType{
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Male),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Young),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Normal),
+ .race = static_cast<u32>(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,
+ RandomMiiData4{
+ .gender = static_cast<u32>(Gender::Female),
+ .age = static_cast<u32>(Age::Old),
+ .race = static_cast<u32>(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{
+const std::array<RandomMiiData2, 3> RandomMiiGlassType{
+ RandomMiiData2{
.arg_1 = 0,
.values_count = 9,
.values = {90, 94, 96, 100, 0, 0, 0, 0, 0},
},
- Service::Mii::RandomMiiData2{
+ RandomMiiData2{
.arg_1 = 1,
.values_count = 9,
.values = {83, 86, 90, 93, 94, 96, 98, 100, 0},
},
- Service::Mii::RandomMiiData2{
+ RandomMiiData2{
.arg_1 = 2,
.values_count = 9,
.values = {78, 83, 0, 93, 0, 0, 98, 100, 0},
},
};
+u8 FromVer3GetFacelineColor(u8 color) {
+ return FromVer3FacelineColorTable[color];
+}
+
+u8 FromVer3GetHairColor(u8 color) {
+ return FromVer3HairColorTable[color];
+}
+
+u8 FromVer3GetEyeColor(u8 color) {
+ return FromVer3EyeColorTable[color];
+}
+
+u8 FromVer3GetMouthlineColor(u8 color) {
+ return FromVer3MouthlineColorTable[color];
+}
+
+u8 FromVer3GetGlassColor(u8 color) {
+ return FromVer3GlassColorTable[color];
+}
+
+u8 FromVer3GetGlassType(u8 type) {
+ return FromVer3GlassTypeTable[type];
+}
+
+FacelineColor GetFacelineColorFromVer3(u32 color) {
+ return static_cast<FacelineColor>(Ver3FacelineColorTable[color]);
+}
+
+CommonColor GetHairColorFromVer3(u32 color) {
+ return static_cast<CommonColor>(Ver3HairColorTable[color]);
+}
+
+CommonColor GetEyeColorFromVer3(u32 color) {
+ return static_cast<CommonColor>(Ver3EyeColorTable[color]);
+}
+
+CommonColor GetMouthColorFromVer3(u32 color) {
+ return static_cast<CommonColor>(Ver3MouthColorTable[color]);
+}
+
+CommonColor GetGlassColorFromVer3(u32 color) {
+ return static_cast<CommonColor>(Ver3GlassColorTable[color]);
+}
+
} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/types/raw_data.h b/src/core/hle/service/mii/types/raw_data.h
new file mode 100644
index 000000000..9a4cfa738
--- /dev/null
+++ b/src/core/hle/service/mii/types/raw_data.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "core/hle/service/mii/mii_types.h"
+
+namespace Service::Mii::RawData {
+
+struct RandomMiiValues {
+ std::array<u8, 188> values{};
+};
+static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
+
+struct RandomMiiData4 {
+ u32 gender{};
+ u32 age{};
+ u32 race{};
+ u32 values_count{};
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
+
+struct RandomMiiData3 {
+ u32 arg_1;
+ u32 arg_2;
+ u32 values_count;
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
+
+struct RandomMiiData2 {
+ u32 arg_1;
+ u32 values_count;
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
+
+extern const std::array<Service::Mii::DefaultMii, 2> BaseMii;
+extern const std::array<Service::Mii::DefaultMii, 6> DefaultMii;
+
+extern const std::array<u8, 62> EyeRotateLookup;
+extern const std::array<u8, 24> EyebrowRotateLookup;
+
+extern const std::array<RandomMiiData4, 18> RandomMiiFaceline;
+extern const std::array<RandomMiiData3, 6> RandomMiiFacelineColor;
+extern const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle;
+extern const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup;
+extern const std::array<RandomMiiData4, 18> RandomMiiHairType;
+extern const std::array<RandomMiiData3, 9> RandomMiiHairColor;
+extern const std::array<RandomMiiData4, 18> RandomMiiEyeType;
+extern const std::array<RandomMiiData2, 3> RandomMiiEyeColor;
+extern const std::array<RandomMiiData4, 18> RandomMiiEyebrowType;
+extern const std::array<RandomMiiData4, 18> RandomMiiNoseType;
+extern const std::array<RandomMiiData4, 18> RandomMiiMouthType;
+extern const std::array<RandomMiiData2, 3> RandomMiiGlassType;
+
+u8 FromVer3GetFacelineColor(u8 color);
+u8 FromVer3GetHairColor(u8 color);
+u8 FromVer3GetEyeColor(u8 color);
+u8 FromVer3GetMouthlineColor(u8 color);
+u8 FromVer3GetGlassColor(u8 color);
+u8 FromVer3GetGlassType(u8 type);
+
+FacelineColor GetFacelineColorFromVer3(u32 color);
+CommonColor GetHairColorFromVer3(u32 color);
+CommonColor GetEyeColorFromVer3(u32 color);
+CommonColor GetMouthColorFromVer3(u32 color);
+CommonColor GetGlassColorFromVer3(u32 color);
+
+} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp
new file mode 100644
index 000000000..8fce636c7
--- /dev/null
+++ b/src/core/hle/service/mii/types/store_data.cpp
@@ -0,0 +1,643 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/mii/mii_util.h"
+#include "core/hle/service/mii/types/raw_data.h"
+#include "core/hle/service/mii/types/store_data.h"
+
+namespace Service::Mii {
+
+void StoreData::BuildDefault(u32 mii_index) {
+ const auto& default_mii = RawData::DefaultMii[mii_index];
+ core_data.SetDefault();
+
+ core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type));
+ core_data.SetFacelineColor(
+ RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color)));
+ core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle));
+ core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup));
+
+ core_data.SetHairType(static_cast<HairType>(default_mii.hair_type));
+ core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color)));
+ core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip));
+ core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type));
+ core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color)));
+ core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale));
+ core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect));
+ core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate));
+ core_data.SetEyeX(static_cast<u8>(default_mii.eye_x));
+ core_data.SetEyeY(static_cast<u8>(default_mii.eye_y));
+
+ core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type));
+ core_data.SetEyebrowColor(
+ RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color)));
+ core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale));
+ core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
+ core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
+ core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
+ core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y));
+
+ core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
+ core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
+ core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
+
+ core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type));
+ core_data.SetMouthColor(
+ RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
+ core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
+ core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect));
+ core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y));
+
+ core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type));
+ core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type));
+ core_data.SetBeardColor(
+ RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color)));
+ core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale));
+ core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y));
+
+ core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type));
+ core_data.SetGlassColor(
+ RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color)));
+ core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale));
+ core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y));
+
+ core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type));
+ core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale));
+ core_data.SetMoleX(static_cast<u8>(default_mii.mole_x));
+ core_data.SetMoleY(static_cast<u8>(default_mii.mole_y));
+
+ core_data.SetHeight(static_cast<u8>(default_mii.height));
+ core_data.SetBuild(static_cast<u8>(default_mii.weight));
+ core_data.SetGender(static_cast<Gender>(default_mii.gender));
+ core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color));
+ core_data.SetRegionMove(static_cast<u8>(default_mii.region_move));
+ core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region));
+ core_data.SetType(static_cast<u8>(default_mii.type));
+ core_data.SetNickname(default_mii.nickname);
+
+ const auto device_id = MiiUtil::GetDeviceId();
+ create_id = MiiUtil::MakeCreateId();
+ device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
+ data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
+}
+
+void StoreData::BuildBase(Gender gender) {
+ const auto& default_mii = RawData::BaseMii[gender == Gender::Female ? 1 : 0];
+ core_data.SetDefault();
+
+ core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type));
+ core_data.SetFacelineColor(
+ RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color)));
+ core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle));
+ core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup));
+
+ core_data.SetHairType(static_cast<HairType>(default_mii.hair_type));
+ core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color)));
+ core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip));
+ core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type));
+ core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color)));
+ core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale));
+ core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect));
+ core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate));
+ core_data.SetEyeX(static_cast<u8>(default_mii.eye_x));
+ core_data.SetEyeY(static_cast<u8>(default_mii.eye_y));
+
+ core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type));
+ core_data.SetEyebrowColor(
+ RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color)));
+ core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale));
+ core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
+ core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
+ core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
+ core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y));
+
+ core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
+ core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
+ core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
+
+ core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type));
+ core_data.SetMouthColor(
+ RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
+ core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
+ core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect));
+ core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y));
+
+ core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type));
+ core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type));
+ core_data.SetBeardColor(
+ RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color)));
+ core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale));
+ core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y));
+
+ core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type));
+ core_data.SetGlassColor(
+ RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color)));
+ core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale));
+ core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y));
+
+ core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type));
+ core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale));
+ core_data.SetMoleX(static_cast<u8>(default_mii.mole_x));
+ core_data.SetMoleY(static_cast<u8>(default_mii.mole_y));
+
+ core_data.SetHeight(static_cast<u8>(default_mii.height));
+ core_data.SetBuild(static_cast<u8>(default_mii.weight));
+ core_data.SetGender(static_cast<Gender>(default_mii.gender));
+ core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color));
+ core_data.SetRegionMove(static_cast<u8>(default_mii.region_move));
+ core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region));
+ core_data.SetType(static_cast<u8>(default_mii.type));
+ core_data.SetNickname(default_mii.nickname);
+
+ const auto device_id = MiiUtil::GetDeviceId();
+ create_id = MiiUtil::MakeCreateId();
+ device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
+ data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
+}
+
+void StoreData::BuildRandom(Age age, Gender gender, Race race) {
+ core_data.BuildRandom(age, gender, race);
+ const auto device_id = MiiUtil::GetDeviceId();
+ create_id = MiiUtil::MakeCreateId();
+ device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
+ data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
+}
+
+void StoreData::SetInvalidName() {
+ const auto& invalid_name = core_data.GetInvalidNickname();
+ const auto device_id = MiiUtil::GetDeviceId();
+ core_data.SetNickname(invalid_name);
+ device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
+ data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
+}
+
+bool StoreData::IsSpecial() const {
+ return GetType() == 1;
+}
+
+u32 StoreData::IsValid() const {
+ // TODO: complete this
+ return 0;
+}
+
+void StoreData::SetFontRegion(FontRegion value) {
+ core_data.SetFontRegion(value);
+}
+
+void StoreData::SetFavoriteColor(FavoriteColor value) {
+ core_data.SetFavoriteColor(value);
+}
+
+void StoreData::SetGender(Gender value) {
+ core_data.SetGender(value);
+}
+
+void StoreData::SetHeight(u8 value) {
+ core_data.SetHeight(value);
+}
+
+void StoreData::SetBuild(u8 value) {
+ core_data.SetBuild(value);
+}
+
+void StoreData::SetType(u8 value) {
+ core_data.SetType(value);
+}
+
+void StoreData::SetRegionMove(u8 value) {
+ core_data.SetRegionMove(value);
+}
+
+void StoreData::SetFacelineType(FacelineType value) {
+ core_data.SetFacelineType(value);
+}
+
+void StoreData::SetFacelineColor(FacelineColor value) {
+ core_data.SetFacelineColor(value);
+}
+
+void StoreData::SetFacelineWrinkle(FacelineWrinkle value) {
+ core_data.SetFacelineWrinkle(value);
+}
+
+void StoreData::SetFacelineMake(FacelineMake value) {
+ core_data.SetFacelineMake(value);
+}
+
+void StoreData::SetHairType(HairType value) {
+ core_data.SetHairType(value);
+}
+
+void StoreData::SetHairColor(CommonColor value) {
+ core_data.SetHairColor(value);
+}
+
+void StoreData::SetHairFlip(HairFlip value) {
+ core_data.SetHairFlip(value);
+}
+
+void StoreData::SetEyeType(EyeType value) {
+ core_data.SetEyeType(value);
+}
+
+void StoreData::SetEyeColor(CommonColor value) {
+ core_data.SetEyeColor(value);
+}
+
+void StoreData::SetEyeScale(u8 value) {
+ core_data.SetEyeScale(value);
+}
+
+void StoreData::SetEyeAspect(u8 value) {
+ core_data.SetEyeAspect(value);
+}
+
+void StoreData::SetEyeRotate(u8 value) {
+ core_data.SetEyeRotate(value);
+}
+
+void StoreData::SetEyeX(u8 value) {
+ core_data.SetEyeX(value);
+}
+
+void StoreData::SetEyeY(u8 value) {
+ core_data.SetEyeY(value);
+}
+
+void StoreData::SetEyebrowType(EyebrowType value) {
+ core_data.SetEyebrowType(value);
+}
+
+void StoreData::SetEyebrowColor(CommonColor value) {
+ core_data.SetEyebrowColor(value);
+}
+
+void StoreData::SetEyebrowScale(u8 value) {
+ core_data.SetEyebrowScale(value);
+}
+
+void StoreData::SetEyebrowAspect(u8 value) {
+ core_data.SetEyebrowAspect(value);
+}
+
+void StoreData::SetEyebrowRotate(u8 value) {
+ core_data.SetEyebrowRotate(value);
+}
+
+void StoreData::SetEyebrowX(u8 value) {
+ core_data.SetEyebrowX(value);
+}
+
+void StoreData::SetEyebrowY(u8 value) {
+ core_data.SetEyebrowY(value);
+}
+
+void StoreData::SetNoseType(NoseType value) {
+ core_data.SetNoseType(value);
+}
+
+void StoreData::SetNoseScale(u8 value) {
+ core_data.SetNoseScale(value);
+}
+
+void StoreData::SetNoseY(u8 value) {
+ core_data.SetNoseY(value);
+}
+
+void StoreData::SetMouthType(u8 value) {
+ core_data.SetMouthType(value);
+}
+
+void StoreData::SetMouthColor(CommonColor value) {
+ core_data.SetMouthColor(value);
+}
+
+void StoreData::SetMouthScale(u8 value) {
+ core_data.SetMouthScale(value);
+}
+
+void StoreData::SetMouthAspect(u8 value) {
+ core_data.SetMouthAspect(value);
+}
+
+void StoreData::SetMouthY(u8 value) {
+ core_data.SetMouthY(value);
+}
+
+void StoreData::SetBeardColor(CommonColor value) {
+ core_data.SetBeardColor(value);
+}
+
+void StoreData::SetBeardType(BeardType value) {
+ core_data.SetBeardType(value);
+}
+
+void StoreData::SetMustacheType(MustacheType value) {
+ core_data.SetMustacheType(value);
+}
+
+void StoreData::SetMustacheScale(u8 value) {
+ core_data.SetMustacheScale(value);
+}
+
+void StoreData::SetMustacheY(u8 value) {
+ core_data.SetMustacheY(value);
+}
+
+void StoreData::SetGlassType(GlassType value) {
+ core_data.SetGlassType(value);
+}
+
+void StoreData::SetGlassColor(CommonColor value) {
+ core_data.SetGlassColor(value);
+}
+
+void StoreData::SetGlassScale(u8 value) {
+ core_data.SetGlassScale(value);
+}
+
+void StoreData::SetGlassY(u8 value) {
+ core_data.SetGlassY(value);
+}
+
+void StoreData::SetMoleType(MoleType value) {
+ core_data.SetMoleType(value);
+}
+
+void StoreData::SetMoleScale(u8 value) {
+ core_data.SetMoleScale(value);
+}
+
+void StoreData::SetMoleX(u8 value) {
+ core_data.SetMoleX(value);
+}
+
+void StoreData::SetMoleY(u8 value) {
+ core_data.SetMoleY(value);
+}
+
+void StoreData::SetNickname(Nickname value) {
+ core_data.SetNickname(value);
+}
+
+Common::UUID StoreData::GetCreateId() const {
+ return create_id;
+}
+
+FontRegion StoreData::GetFontRegion() const {
+ return static_cast<FontRegion>(core_data.GetFontRegion());
+}
+
+FavoriteColor StoreData::GetFavoriteColor() const {
+ return core_data.GetFavoriteColor();
+}
+
+Gender StoreData::GetGender() const {
+ return core_data.GetGender();
+}
+
+u8 StoreData::GetHeight() const {
+ return core_data.GetHeight();
+}
+
+u8 StoreData::GetBuild() const {
+ return core_data.GetBuild();
+}
+
+u8 StoreData::GetType() const {
+ return core_data.GetType();
+}
+
+u8 StoreData::GetRegionMove() const {
+ return core_data.GetRegionMove();
+}
+
+FacelineType StoreData::GetFacelineType() const {
+ return core_data.GetFacelineType();
+}
+
+FacelineColor StoreData::GetFacelineColor() const {
+ return core_data.GetFacelineColor();
+}
+
+FacelineWrinkle StoreData::GetFacelineWrinkle() const {
+ return core_data.GetFacelineWrinkle();
+}
+
+FacelineMake StoreData::GetFacelineMake() const {
+ return core_data.GetFacelineMake();
+}
+
+HairType StoreData::GetHairType() const {
+ return core_data.GetHairType();
+}
+
+CommonColor StoreData::GetHairColor() const {
+ return core_data.GetHairColor();
+}
+
+HairFlip StoreData::GetHairFlip() const {
+ return core_data.GetHairFlip();
+}
+
+EyeType StoreData::GetEyeType() const {
+ return core_data.GetEyeType();
+}
+
+CommonColor StoreData::GetEyeColor() const {
+ return core_data.GetEyeColor();
+}
+
+u8 StoreData::GetEyeScale() const {
+ return core_data.GetEyeScale();
+}
+
+u8 StoreData::GetEyeAspect() const {
+ return core_data.GetEyeAspect();
+}
+
+u8 StoreData::GetEyeRotate() const {
+ return core_data.GetEyeRotate();
+}
+
+u8 StoreData::GetEyeX() const {
+ return core_data.GetEyeX();
+}
+
+u8 StoreData::GetEyeY() const {
+ return core_data.GetEyeY();
+}
+
+EyebrowType StoreData::GetEyebrowType() const {
+ return core_data.GetEyebrowType();
+}
+
+CommonColor StoreData::GetEyebrowColor() const {
+ return core_data.GetEyebrowColor();
+}
+
+u8 StoreData::GetEyebrowScale() const {
+ return core_data.GetEyebrowScale();
+}
+
+u8 StoreData::GetEyebrowAspect() const {
+ return core_data.GetEyebrowAspect();
+}
+
+u8 StoreData::GetEyebrowRotate() const {
+ return core_data.GetEyebrowRotate();
+}
+
+u8 StoreData::GetEyebrowX() const {
+ return core_data.GetEyebrowX();
+}
+
+u8 StoreData::GetEyebrowY() const {
+ return core_data.GetEyebrowY();
+}
+
+NoseType StoreData::GetNoseType() const {
+ return core_data.GetNoseType();
+}
+
+u8 StoreData::GetNoseScale() const {
+ return core_data.GetNoseScale();
+}
+
+u8 StoreData::GetNoseY() const {
+ return core_data.GetNoseY();
+}
+
+MouthType StoreData::GetMouthType() const {
+ return core_data.GetMouthType();
+}
+
+CommonColor StoreData::GetMouthColor() const {
+ return core_data.GetMouthColor();
+}
+
+u8 StoreData::GetMouthScale() const {
+ return core_data.GetMouthScale();
+}
+
+u8 StoreData::GetMouthAspect() const {
+ return core_data.GetMouthAspect();
+}
+
+u8 StoreData::GetMouthY() const {
+ return core_data.GetMouthY();
+}
+
+CommonColor StoreData::GetBeardColor() const {
+ return core_data.GetBeardColor();
+}
+
+BeardType StoreData::GetBeardType() const {
+ return core_data.GetBeardType();
+}
+
+MustacheType StoreData::GetMustacheType() const {
+ return core_data.GetMustacheType();
+}
+
+u8 StoreData::GetMustacheScale() const {
+ return core_data.GetMustacheScale();
+}
+
+u8 StoreData::GetMustacheY() const {
+ return core_data.GetMustacheY();
+}
+
+GlassType StoreData::GetGlassType() const {
+ return core_data.GetGlassType();
+}
+
+CommonColor StoreData::GetGlassColor() const {
+ return core_data.GetGlassColor();
+}
+
+u8 StoreData::GetGlassScale() const {
+ return core_data.GetGlassScale();
+}
+
+u8 StoreData::GetGlassY() const {
+ return core_data.GetGlassY();
+}
+
+MoleType StoreData::GetMoleType() const {
+ return core_data.GetMoleType();
+}
+
+u8 StoreData::GetMoleScale() const {
+ return core_data.GetMoleScale();
+}
+
+u8 StoreData::GetMoleX() const {
+ return core_data.GetMoleX();
+}
+
+u8 StoreData::GetMoleY() const {
+ return core_data.GetMoleY();
+}
+
+Nickname StoreData::GetNickname() const {
+ return core_data.GetNickname();
+}
+
+bool StoreData::operator==(const StoreData& data) {
+ bool is_identical = data.core_data.IsValid() == 0;
+ is_identical &= core_data.GetNickname().data == data.core_data.GetNickname().data;
+ is_identical &= GetCreateId() == data.GetCreateId();
+ is_identical &= GetFontRegion() == data.GetFontRegion();
+ is_identical &= GetFavoriteColor() == data.GetFavoriteColor();
+ is_identical &= GetGender() == data.GetGender();
+ is_identical &= GetHeight() == data.GetHeight();
+ is_identical &= GetBuild() == data.GetBuild();
+ is_identical &= GetType() == data.GetType();
+ is_identical &= GetRegionMove() == data.GetRegionMove();
+ is_identical &= GetFacelineType() == data.GetFacelineType();
+ is_identical &= GetFacelineColor() == data.GetFacelineColor();
+ is_identical &= GetFacelineWrinkle() == data.GetFacelineWrinkle();
+ is_identical &= GetFacelineMake() == data.GetFacelineMake();
+ is_identical &= GetHairType() == data.GetHairType();
+ is_identical &= GetHairColor() == data.GetHairColor();
+ is_identical &= GetHairFlip() == data.GetHairFlip();
+ is_identical &= GetEyeType() == data.GetEyeType();
+ is_identical &= GetEyeColor() == data.GetEyeColor();
+ is_identical &= GetEyeScale() == data.GetEyeScale();
+ is_identical &= GetEyeAspect() == data.GetEyeAspect();
+ is_identical &= GetEyeRotate() == data.GetEyeRotate();
+ is_identical &= GetEyeX() == data.GetEyeX();
+ is_identical &= GetEyeY() == data.GetEyeY();
+ is_identical &= GetEyebrowType() == data.GetEyebrowType();
+ is_identical &= GetEyebrowColor() == data.GetEyebrowColor();
+ is_identical &= GetEyebrowScale() == data.GetEyebrowScale();
+ is_identical &= GetEyebrowAspect() == data.GetEyebrowAspect();
+ is_identical &= GetEyebrowRotate() == data.GetEyebrowRotate();
+ is_identical &= GetEyebrowX() == data.GetEyebrowX();
+ is_identical &= GetEyebrowY() == data.GetEyebrowY();
+ is_identical &= GetNoseType() == data.GetNoseType();
+ is_identical &= GetNoseScale() == data.GetNoseScale();
+ is_identical &= GetNoseY() == data.GetNoseY();
+ is_identical &= GetMouthType() == data.GetMouthType();
+ is_identical &= GetMouthColor() == data.GetMouthColor();
+ is_identical &= GetMouthScale() == data.GetMouthScale();
+ is_identical &= GetMouthAspect() == data.GetMouthAspect();
+ is_identical &= GetMouthY() == data.GetMouthY();
+ is_identical &= GetBeardColor() == data.GetBeardColor();
+ is_identical &= GetBeardType() == data.GetBeardType();
+ is_identical &= GetMustacheType() == data.GetMustacheType();
+ is_identical &= GetMustacheScale() == data.GetMustacheScale();
+ is_identical &= GetMustacheY() == data.GetMustacheY();
+ is_identical &= GetGlassType() == data.GetGlassType();
+ is_identical &= GetGlassColor() == data.GetGlassColor();
+ is_identical &= GetGlassScale() == data.GetGlassScale();
+ is_identical &= GetGlassY() == data.GetGlassY();
+ is_identical &= GetMoleType() == data.GetMoleType();
+ is_identical &= GetMoleScale() == data.GetMoleScale();
+ is_identical &= GetMoleX() == data.GetMoleX();
+ is_identical &= data.GetMoleY() == data.GetMoleY();
+ return is_identical;
+}
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h
new file mode 100644
index 000000000..224c32cf8
--- /dev/null
+++ b/src/core/hle/service/mii/types/store_data.h
@@ -0,0 +1,145 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/mii/mii_types.h"
+#include "core/hle/service/mii/types/core_data.h"
+
+namespace Service::Mii {
+
+class StoreData {
+public:
+ // nn::mii::detail::StoreDataRaw::BuildDefault
+ void BuildDefault(u32 mii_index);
+ // nn::mii::detail::StoreDataRaw::BuildDefault
+
+ void BuildBase(Gender gender);
+ // nn::mii::detail::StoreDataRaw::BuildRandom
+ void BuildRandom(Age age, Gender gender, Race race);
+
+ bool IsSpecial() const;
+
+ u32 IsValid() const;
+
+ void SetFontRegion(FontRegion value);
+ void SetFavoriteColor(FavoriteColor value);
+ void SetGender(Gender value);
+ void SetHeight(u8 value);
+ void SetBuild(u8 value);
+ void SetType(u8 value);
+ void SetRegionMove(u8 value);
+ void SetFacelineType(FacelineType value);
+ void SetFacelineColor(FacelineColor value);
+ void SetFacelineWrinkle(FacelineWrinkle value);
+ void SetFacelineMake(FacelineMake value);
+ void SetHairType(HairType value);
+ void SetHairColor(CommonColor value);
+ void SetHairFlip(HairFlip value);
+ void SetEyeType(EyeType value);
+ void SetEyeColor(CommonColor value);
+ void SetEyeScale(u8 value);
+ void SetEyeAspect(u8 value);
+ void SetEyeRotate(u8 value);
+ void SetEyeX(u8 value);
+ void SetEyeY(u8 value);
+ void SetEyebrowType(EyebrowType value);
+ void SetEyebrowColor(CommonColor value);
+ void SetEyebrowScale(u8 value);
+ void SetEyebrowAspect(u8 value);
+ void SetEyebrowRotate(u8 value);
+ void SetEyebrowX(u8 value);
+ void SetEyebrowY(u8 value);
+ void SetNoseType(NoseType value);
+ void SetNoseScale(u8 value);
+ void SetNoseY(u8 value);
+ void SetMouthType(u8 value);
+ void SetMouthColor(CommonColor value);
+ void SetMouthScale(u8 value);
+ void SetMouthAspect(u8 value);
+ void SetMouthY(u8 value);
+ void SetBeardColor(CommonColor value);
+ void SetBeardType(BeardType value);
+ void SetMustacheType(MustacheType value);
+ void SetMustacheScale(u8 value);
+ void SetMustacheY(u8 value);
+ void SetGlassType(GlassType value);
+ void SetGlassColor(CommonColor value);
+ void SetGlassScale(u8 value);
+ void SetGlassY(u8 value);
+ void SetMoleType(MoleType value);
+ void SetMoleScale(u8 value);
+ void SetMoleX(u8 value);
+ void SetMoleY(u8 value);
+ void SetNickname(Nickname nickname);
+ void SetInvalidName();
+
+ Common::UUID GetCreateId() const;
+ FontRegion GetFontRegion() const;
+ FavoriteColor GetFavoriteColor() const;
+ Gender GetGender() const;
+ u8 GetHeight() const;
+ u8 GetBuild() const;
+ u8 GetType() const;
+ u8 GetRegionMove() const;
+ FacelineType GetFacelineType() const;
+ FacelineColor GetFacelineColor() const;
+ FacelineWrinkle GetFacelineWrinkle() const;
+ FacelineMake GetFacelineMake() const;
+ HairType GetHairType() const;
+ CommonColor GetHairColor() const;
+ HairFlip GetHairFlip() const;
+ EyeType GetEyeType() const;
+ CommonColor GetEyeColor() const;
+ u8 GetEyeScale() const;
+ u8 GetEyeAspect() const;
+ u8 GetEyeRotate() const;
+ u8 GetEyeX() const;
+ u8 GetEyeY() const;
+ EyebrowType GetEyebrowType() const;
+ CommonColor GetEyebrowColor() const;
+ u8 GetEyebrowScale() const;
+ u8 GetEyebrowAspect() const;
+ u8 GetEyebrowRotate() const;
+ u8 GetEyebrowX() const;
+ u8 GetEyebrowY() const;
+ NoseType GetNoseType() const;
+ u8 GetNoseScale() const;
+ u8 GetNoseY() const;
+ MouthType GetMouthType() const;
+ CommonColor GetMouthColor() const;
+ u8 GetMouthScale() const;
+ u8 GetMouthAspect() const;
+ u8 GetMouthY() const;
+ CommonColor GetBeardColor() const;
+ BeardType GetBeardType() const;
+ MustacheType GetMustacheType() const;
+ u8 GetMustacheScale() const;
+ u8 GetMustacheY() const;
+ GlassType GetGlassType() const;
+ CommonColor GetGlassColor() const;
+ u8 GetGlassScale() const;
+ u8 GetGlassY() const;
+ MoleType GetMoleType() const;
+ u8 GetMoleScale() const;
+ u8 GetMoleX() const;
+ u8 GetMoleY() const;
+ Nickname GetNickname() const;
+
+ bool operator==(const StoreData& data);
+
+private:
+ CoreData core_data{};
+ Common::UUID create_id{};
+ u16 data_crc{};
+ u16 device_crc{};
+};
+static_assert(sizeof(StoreData) == 0x44, "StoreData has incorrect size.");
+
+struct StoreDataElement {
+ StoreData store_data{};
+ Source source{};
+};
+static_assert(sizeof(StoreDataElement) == 0x48, "StoreDataElement has incorrect size.");
+
+}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp
new file mode 100644
index 000000000..1c28e0b1b
--- /dev/null
+++ b/src/core/hle/service/mii/types/ver3_store_data.cpp
@@ -0,0 +1,241 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/mii/mii_util.h"
+#include "core/hle/service/mii/types/raw_data.h"
+#include "core/hle/service/mii/types/store_data.h"
+#include "core/hle/service/mii/types/ver3_store_data.h"
+
+namespace Service::Mii {
+
+void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) {
+ faceline_color = static_cast<u8>(store_data.GetFacelineColor()) & 0xf;
+ hair_color = static_cast<u8>(store_data.GetHairColor()) & 0x7f;
+ eye_color = static_cast<u8>(store_data.GetEyeColor()) & 0x7f;
+ eyebrow_color = static_cast<u8>(store_data.GetEyebrowColor()) & 0x7f;
+ mouth_color = static_cast<u8>(store_data.GetMouthColor()) & 0x7f;
+ beard_color = static_cast<u8>(store_data.GetBeardColor()) & 0x7f;
+ glass_color = static_cast<u8>(store_data.GetGlassColor()) & 0x7f;
+ glass_type = static_cast<u8>(store_data.GetGlassType()) & 0x1f;
+}
+
+void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const {
+ out_store_data.BuildBase(Gender::Male);
+
+ if (!IsValid()) {
+ return;
+ }
+
+ // TODO: We are ignoring a bunch of data from the mii_v3
+
+ out_store_data.SetGender(static_cast<Gender>(mii_information.gender.Value()));
+ out_store_data.SetFavoriteColor(
+ static_cast<FavoriteColor>(mii_information.favorite_color.Value()));
+ out_store_data.SetHeight(height);
+ out_store_data.SetBuild(build);
+
+ out_store_data.SetNickname(mii_name);
+ out_store_data.SetFontRegion(
+ static_cast<FontRegion>(static_cast<u8>(region_information.font_region)));
+
+ out_store_data.SetFacelineType(
+ static_cast<FacelineType>(appearance_bits1.faceline_type.Value()));
+ out_store_data.SetFacelineColor(
+ static_cast<FacelineColor>(appearance_bits1.faceline_color.Value()));
+ out_store_data.SetFacelineWrinkle(
+ static_cast<FacelineWrinkle>(appearance_bits2.faceline_wrinkle.Value()));
+ out_store_data.SetFacelineMake(
+ static_cast<FacelineMake>(appearance_bits2.faceline_make.Value()));
+
+ out_store_data.SetHairType(static_cast<HairType>(hair_type));
+ out_store_data.SetHairColor(static_cast<CommonColor>(appearance_bits3.hair_color.Value()));
+ out_store_data.SetHairFlip(static_cast<HairFlip>(appearance_bits3.hair_flip.Value()));
+
+ out_store_data.SetEyeType(static_cast<EyeType>(appearance_bits4.eye_type.Value()));
+ out_store_data.SetEyeColor(static_cast<CommonColor>(appearance_bits4.eye_color.Value()));
+ out_store_data.SetEyeScale(static_cast<u8>(appearance_bits4.eye_scale));
+ out_store_data.SetEyeAspect(static_cast<u8>(appearance_bits4.eye_aspect));
+ out_store_data.SetEyeRotate(static_cast<u8>(appearance_bits4.eye_rotate));
+ out_store_data.SetEyeX(static_cast<u8>(appearance_bits4.eye_x));
+ out_store_data.SetEyeY(static_cast<u8>(appearance_bits4.eye_y));
+
+ out_store_data.SetEyebrowType(static_cast<EyebrowType>(appearance_bits5.eyebrow_type.Value()));
+ out_store_data.SetEyebrowColor(
+ static_cast<CommonColor>(appearance_bits5.eyebrow_color.Value()));
+ out_store_data.SetEyebrowScale(static_cast<u8>(appearance_bits5.eyebrow_scale));
+ out_store_data.SetEyebrowAspect(static_cast<u8>(appearance_bits5.eyebrow_aspect));
+ out_store_data.SetEyebrowRotate(static_cast<u8>(appearance_bits5.eyebrow_rotate));
+ out_store_data.SetEyebrowX(static_cast<u8>(appearance_bits5.eyebrow_x));
+ out_store_data.SetEyebrowY(static_cast<u8>(appearance_bits5.eyebrow_y));
+
+ out_store_data.SetNoseType(static_cast<NoseType>(appearance_bits6.nose_type.Value()));
+ out_store_data.SetNoseScale(static_cast<u8>(appearance_bits6.nose_scale));
+ out_store_data.SetNoseY(static_cast<u8>(appearance_bits6.nose_y));
+
+ out_store_data.SetMouthType(static_cast<u8>(appearance_bits7.mouth_type));
+ out_store_data.SetMouthColor(static_cast<CommonColor>(appearance_bits7.mouth_color.Value()));
+ out_store_data.SetMouthScale(static_cast<u8>(appearance_bits7.mouth_scale));
+ out_store_data.SetMouthAspect(static_cast<u8>(appearance_bits7.mouth_aspect));
+ out_store_data.SetMouthY(static_cast<u8>(appearance_bits8.mouth_y));
+
+ out_store_data.SetMustacheType(
+ static_cast<MustacheType>(appearance_bits8.mustache_type.Value()));
+ out_store_data.SetMustacheScale(static_cast<u8>(appearance_bits9.mustache_scale));
+ out_store_data.SetMustacheY(static_cast<u8>(appearance_bits9.mustache_y));
+
+ out_store_data.SetBeardType(static_cast<BeardType>(appearance_bits9.beard_type.Value()));
+ out_store_data.SetBeardColor(static_cast<CommonColor>(appearance_bits9.beard_color.Value()));
+
+ out_store_data.SetGlassType(static_cast<GlassType>(appearance_bits10.glass_type.Value()));
+ out_store_data.SetGlassColor(static_cast<CommonColor>(appearance_bits10.glass_color.Value()));
+ out_store_data.SetGlassScale(static_cast<u8>(appearance_bits10.glass_scale));
+ out_store_data.SetGlassY(static_cast<u8>(appearance_bits10.glass_y));
+
+ out_store_data.SetMoleType(static_cast<MoleType>(appearance_bits11.mole_type.Value()));
+ out_store_data.SetMoleScale(static_cast<u8>(appearance_bits11.mole_scale));
+ out_store_data.SetMoleX(static_cast<u8>(appearance_bits11.mole_x));
+ out_store_data.SetMoleY(static_cast<u8>(appearance_bits11.mole_y));
+}
+
+void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) {
+ version = 1;
+ mii_information.gender.Assign(static_cast<u8>(store_data.GetGender()));
+ mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor()));
+ height = store_data.GetHeight();
+ build = store_data.GetBuild();
+
+ mii_name = store_data.GetNickname();
+ region_information.font_region.Assign(static_cast<u8>(store_data.GetFontRegion()));
+
+ appearance_bits1.faceline_type.Assign(static_cast<u8>(store_data.GetFacelineType()));
+ appearance_bits2.faceline_wrinkle.Assign(static_cast<u8>(store_data.GetFacelineWrinkle()));
+ appearance_bits2.faceline_make.Assign(static_cast<u8>(store_data.GetFacelineMake()));
+
+ hair_type = static_cast<u8>(store_data.GetHairType());
+ appearance_bits3.hair_flip.Assign(static_cast<u8>(store_data.GetHairFlip()));
+
+ appearance_bits4.eye_type.Assign(static_cast<u8>(store_data.GetEyeType()));
+ appearance_bits4.eye_scale.Assign(store_data.GetEyeScale());
+ appearance_bits4.eye_aspect.Assign(store_data.GetEyebrowAspect());
+ appearance_bits4.eye_rotate.Assign(store_data.GetEyeRotate());
+ appearance_bits4.eye_x.Assign(store_data.GetEyeX());
+ appearance_bits4.eye_y.Assign(store_data.GetEyeY());
+
+ appearance_bits5.eyebrow_type.Assign(static_cast<u8>(store_data.GetEyebrowType()));
+ appearance_bits5.eyebrow_scale.Assign(store_data.GetEyebrowScale());
+ appearance_bits5.eyebrow_aspect.Assign(store_data.GetEyebrowAspect());
+ appearance_bits5.eyebrow_rotate.Assign(store_data.GetEyebrowRotate());
+ appearance_bits5.eyebrow_x.Assign(store_data.GetEyebrowX());
+ appearance_bits5.eyebrow_y.Assign(store_data.GetEyebrowY());
+
+ appearance_bits6.nose_type.Assign(static_cast<u8>(store_data.GetNoseType()));
+ appearance_bits6.nose_scale.Assign(store_data.GetNoseScale());
+ appearance_bits6.nose_y.Assign(store_data.GetNoseY());
+
+ appearance_bits7.mouth_type.Assign(static_cast<u8>(store_data.GetMouthType()));
+ appearance_bits7.mouth_scale.Assign(store_data.GetMouthScale());
+ appearance_bits7.mouth_aspect.Assign(store_data.GetMouthAspect());
+ appearance_bits8.mouth_y.Assign(store_data.GetMouthY());
+
+ appearance_bits8.mustache_type.Assign(static_cast<u8>(store_data.GetMustacheType()));
+ appearance_bits9.mustache_scale.Assign(store_data.GetMustacheScale());
+ appearance_bits9.mustache_y.Assign(store_data.GetMustacheY());
+
+ appearance_bits9.beard_type.Assign(static_cast<u8>(store_data.GetBeardType()));
+
+ appearance_bits10.glass_scale.Assign(store_data.GetGlassScale());
+ appearance_bits10.glass_y.Assign(store_data.GetGlassY());
+
+ appearance_bits11.mole_type.Assign(static_cast<u8>(store_data.GetMoleType()));
+ appearance_bits11.mole_scale.Assign(store_data.GetMoleScale());
+ appearance_bits11.mole_x.Assign(store_data.GetMoleX());
+ appearance_bits11.mole_y.Assign(store_data.GetMoleY());
+
+ // These types are converted to V3 from a table
+ appearance_bits1.faceline_color.Assign(
+ RawData::FromVer3GetFacelineColor(static_cast<u8>(store_data.GetFacelineColor())));
+ appearance_bits3.hair_color.Assign(
+ RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetHairColor())));
+ appearance_bits4.eye_color.Assign(
+ RawData::FromVer3GetEyeColor(static_cast<u8>(store_data.GetEyeColor())));
+ appearance_bits5.eyebrow_color.Assign(
+ RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetEyebrowColor())));
+ appearance_bits7.mouth_color.Assign(
+ RawData::FromVer3GetMouthlineColor(static_cast<u8>(store_data.GetMouthColor())));
+ appearance_bits9.beard_color.Assign(
+ RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetBeardColor())));
+ appearance_bits10.glass_color.Assign(
+ RawData::FromVer3GetGlassColor(static_cast<u8>(store_data.GetGlassColor())));
+ appearance_bits10.glass_type.Assign(
+ RawData::FromVer3GetGlassType(static_cast<u8>(store_data.GetGlassType())));
+
+ crc = MiiUtil::CalculateCrc16(&version, sizeof(Ver3StoreData) - sizeof(u16));
+}
+
+u32 Ver3StoreData::IsValid() const {
+ bool is_valid = version == 0 || version == 3;
+
+ is_valid = is_valid && (mii_name.data[0] != '\0');
+
+ is_valid = is_valid && (mii_information.birth_month < 13);
+ is_valid = is_valid && (mii_information.birth_day < 32);
+ is_valid = is_valid && (mii_information.favorite_color <= static_cast<u8>(FavoriteColor::Max));
+ is_valid = is_valid && (height <= MaxHeight);
+ is_valid = is_valid && (build <= MaxBuild);
+
+ is_valid = is_valid && (appearance_bits1.faceline_type <= static_cast<u8>(FacelineType::Max));
+ is_valid = is_valid && (appearance_bits1.faceline_color <= MaxVer3CommonColor - 2);
+ is_valid =
+ is_valid && (appearance_bits2.faceline_wrinkle <= static_cast<u8>(FacelineWrinkle::Max));
+ is_valid = is_valid && (appearance_bits2.faceline_make <= static_cast<u8>(FacelineMake::Max));
+
+ is_valid = is_valid && (hair_type <= static_cast<u8>(HairType::Max));
+ is_valid = is_valid && (appearance_bits3.hair_color <= MaxVer3CommonColor);
+
+ is_valid = is_valid && (appearance_bits4.eye_type <= static_cast<u8>(EyeType::Max));
+ is_valid = is_valid && (appearance_bits4.eye_color <= MaxVer3CommonColor - 2);
+ is_valid = is_valid && (appearance_bits4.eye_scale <= MaxEyeScale);
+ is_valid = is_valid && (appearance_bits4.eye_aspect <= MaxEyeAspect);
+ is_valid = is_valid && (appearance_bits4.eye_rotate <= MaxEyeRotate);
+ is_valid = is_valid && (appearance_bits4.eye_x <= MaxEyeX);
+ is_valid = is_valid && (appearance_bits4.eye_y <= MaxEyeY);
+
+ is_valid = is_valid && (appearance_bits5.eyebrow_type <= static_cast<u8>(EyebrowType::Max));
+ is_valid = is_valid && (appearance_bits5.eyebrow_color <= MaxVer3CommonColor);
+ is_valid = is_valid && (appearance_bits5.eyebrow_scale <= MaxEyebrowScale);
+ is_valid = is_valid && (appearance_bits5.eyebrow_aspect <= MaxEyebrowAspect);
+ is_valid = is_valid && (appearance_bits5.eyebrow_rotate <= MaxEyebrowRotate);
+ is_valid = is_valid && (appearance_bits5.eyebrow_x <= MaxEyebrowX);
+ is_valid = is_valid && (appearance_bits5.eyebrow_y <= MaxEyebrowY);
+
+ is_valid = is_valid && (appearance_bits6.nose_type <= static_cast<u8>(NoseType::Max));
+ is_valid = is_valid && (appearance_bits6.nose_scale <= MaxNoseScale);
+ is_valid = is_valid && (appearance_bits6.nose_y <= MaxNoseY);
+
+ is_valid = is_valid && (appearance_bits7.mouth_type <= static_cast<u8>(MouthType::Max));
+ is_valid = is_valid && (appearance_bits7.mouth_color <= MaxVer3CommonColor - 3);
+ is_valid = is_valid && (appearance_bits7.mouth_scale <= MaxMouthScale);
+ is_valid = is_valid && (appearance_bits7.mouth_aspect <= MaxMoutAspect);
+ is_valid = is_valid && (appearance_bits8.mouth_y <= MaxMouthY);
+
+ is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast<u8>(MustacheType::Max));
+ is_valid = is_valid && (appearance_bits9.mustache_scale < MaxMustacheScale);
+ is_valid = is_valid && (appearance_bits9.mustache_y <= MasMustacheY);
+
+ is_valid = is_valid && (appearance_bits9.beard_type <= static_cast<u8>(BeardType::Max));
+ is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor);
+
+ is_valid = is_valid && (appearance_bits10.glass_type <= MaxVer3GlassType);
+ is_valid = is_valid && (appearance_bits10.glass_color <= MaxVer3CommonColor - 2);
+ is_valid = is_valid && (appearance_bits10.glass_scale <= MaxGlassScale);
+ is_valid = is_valid && (appearance_bits10.glass_y <= MaxGlassScale);
+
+ is_valid = is_valid && (appearance_bits11.mole_type <= static_cast<u8>(MoleType::Max));
+ is_valid = is_valid && (appearance_bits11.mole_scale <= MaxMoleScale);
+ is_valid = is_valid && (appearance_bits11.mole_x <= MaxMoleX);
+ is_valid = is_valid && (appearance_bits11.mole_y <= MaxMoleY);
+
+ return is_valid;
+}
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/ver3_store_data.h b/src/core/hle/service/mii/types/ver3_store_data.h
new file mode 100644
index 000000000..47907bf7d
--- /dev/null
+++ b/src/core/hle/service/mii/types/ver3_store_data.h
@@ -0,0 +1,160 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/mii/mii_types.h"
+
+namespace Service::Mii {
+class StoreData;
+
+// This is nn::mii::Ver3StoreData
+// Based on citra HLE::Applets::MiiData and PretendoNetwork.
+// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
+// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
+
+struct NfpStoreDataExtension {
+ void SetFromStoreData(const StoreData& store_data);
+
+ u8 faceline_color;
+ u8 hair_color;
+ u8 eye_color;
+ u8 eyebrow_color;
+ u8 mouth_color;
+ u8 beard_color;
+ u8 glass_color;
+ u8 glass_type;
+};
+static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size");
+
+#pragma pack(push, 4)
+class Ver3StoreData {
+public:
+ void BuildToStoreData(StoreData& out_store_data) const;
+ void BuildFromStoreData(const StoreData& store_data);
+
+ u32 IsValid() const;
+
+ u8 version;
+ union {
+ u8 raw;
+
+ BitField<0, 1, u8> allow_copying;
+ BitField<1, 1, u8> profanity_flag;
+ BitField<2, 2, u8> region_lock;
+ BitField<4, 2, u8> font_region;
+ } region_information;
+ u16_be mii_id;
+ u64_be system_id;
+ u32_be specialness_and_creation_date;
+ std::array<u8, 6> creator_mac;
+ u16_be padding;
+ union {
+ u16 raw;
+
+ BitField<0, 1, u16> gender;
+ BitField<1, 4, u16> birth_month;
+ BitField<5, 5, u16> birth_day;
+ BitField<10, 4, u16> favorite_color;
+ BitField<14, 1, u16> favorite;
+ } mii_information;
+ Nickname mii_name;
+ u8 height;
+ u8 build;
+ union {
+ u8 raw;
+
+ BitField<0, 1, u8> disable_sharing;
+ BitField<1, 4, u8> faceline_type;
+ BitField<5, 3, u8> faceline_color;
+ } appearance_bits1;
+ union {
+ u8 raw;
+
+ BitField<0, 4, u8> faceline_wrinkle;
+ BitField<4, 4, u8> faceline_make;
+ } appearance_bits2;
+ u8 hair_type;
+ union {
+ u8 raw;
+
+ BitField<0, 3, u8> hair_color;
+ BitField<3, 1, u8> hair_flip;
+ } appearance_bits3;
+ union {
+ u32 raw;
+
+ BitField<0, 6, u32> eye_type;
+ BitField<6, 3, u32> eye_color;
+ BitField<9, 4, u32> eye_scale;
+ BitField<13, 3, u32> eye_aspect;
+ BitField<16, 5, u32> eye_rotate;
+ BitField<21, 4, u32> eye_x;
+ BitField<25, 5, u32> eye_y;
+ } appearance_bits4;
+ union {
+ u32 raw;
+
+ BitField<0, 5, u32> eyebrow_type;
+ BitField<5, 3, u32> eyebrow_color;
+ BitField<8, 4, u32> eyebrow_scale;
+ BitField<12, 3, u32> eyebrow_aspect;
+ BitField<16, 4, u32> eyebrow_rotate;
+ BitField<21, 4, u32> eyebrow_x;
+ BitField<25, 5, u32> eyebrow_y;
+ } appearance_bits5;
+ union {
+ u16 raw;
+
+ BitField<0, 5, u16> nose_type;
+ BitField<5, 4, u16> nose_scale;
+ BitField<9, 5, u16> nose_y;
+ } appearance_bits6;
+ union {
+ u16 raw;
+
+ BitField<0, 6, u16> mouth_type;
+ BitField<6, 3, u16> mouth_color;
+ BitField<9, 4, u16> mouth_scale;
+ BitField<13, 3, u16> mouth_aspect;
+ } appearance_bits7;
+ union {
+ u8 raw;
+
+ BitField<0, 5, u8> mouth_y;
+ BitField<5, 3, u8> mustache_type;
+ } appearance_bits8;
+ u8 allow_copying;
+ union {
+ u16 raw;
+
+ BitField<0, 3, u16> beard_type;
+ BitField<3, 3, u16> beard_color;
+ BitField<6, 4, u16> mustache_scale;
+ BitField<10, 5, u16> mustache_y;
+ } appearance_bits9;
+ union {
+ u16 raw;
+
+ BitField<0, 4, u16> glass_type;
+ BitField<4, 3, u16> glass_color;
+ BitField<7, 4, u16> glass_scale;
+ BitField<11, 5, u16> glass_y;
+ } appearance_bits10;
+ union {
+ u16 raw;
+
+ BitField<0, 1, u16> mole_type;
+ BitField<1, 4, u16> mole_scale;
+ BitField<5, 5, u16> mole_x;
+ BitField<10, 5, u16> mole_y;
+ } appearance_bits11;
+
+ Nickname author_name;
+ INSERT_PADDING_BYTES(0x2);
+ u16_be crc;
+};
+static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
+#pragma pack(pop)
+
+}; // namespace Service::Mii
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 5df40f9a0..5dda12343 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -28,7 +28,6 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/mii/mii_manager.h"
-#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfc/common/amiibo_crypto.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/mifare_result.h"
@@ -681,12 +680,16 @@ Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const {
return ResultRegistrationIsNotInitialized;
}
- Service::Mii::MiiManager manager;
+ Mii::CharInfo char_info{};
+ Mii::StoreData store_data{};
+ tag_data.owner_mii.BuildToStoreData(store_data);
+ char_info.SetFromStoreData(store_data);
+
const auto& settings = tag_data.settings;
// TODO: Validate this data
register_info = {
- .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
+ .mii_char_info = char_info,
.creation_date = settings.init_date.GetWriteDate(),
.amiibo_name = GetAmiiboName(settings),
.font_region = settings.settings.font_region,
@@ -825,8 +828,11 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
return ResultWrongDeviceState;
}
- Service::Mii::MiiManager manager;
- const auto mii = manager.BuildBase(Mii::Gender::Male);
+ Service::Mii::StoreData store_data{};
+ Service::Mii::NfpStoreDataExtension extension{};
+ store_data.BuildBase(Mii::Gender::Male);
+ extension.SetFromStoreData(store_data);
+
auto& settings = tag_data.settings;
if (tag_data.settings.settings.amiibo_initialized == 0) {
@@ -835,8 +841,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
}
SetAmiiboName(settings, register_info.amiibo_name);
- tag_data.owner_mii = manager.BuildFromStoreData(mii);
- tag_data.mii_extension = manager.SetFromStoreData(mii);
+ tag_data.owner_mii.BuildFromStoreData(store_data);
+ tag_data.mii_extension = extension;
tag_data.unknown = 0;
tag_data.unknown2 = {};
settings.country_code_id = 0;
@@ -1453,7 +1459,7 @@ void NfcDevice::UpdateRegisterInfoCrc() {
void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
const NFP::EncryptedNTAG215File& encrypted_file) const {
- Service::Mii::MiiManager manager;
+ Service::Mii::StoreData store_data{};
auto& settings = stubbed_tag_data.settings;
stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file);
@@ -1467,7 +1473,8 @@ void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'});
settings.settings.font_region.Assign(0);
settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
- stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildBase(Mii::Gender::Male));
+ store_data.BuildBase(Mii::Gender::Male);
+ stubbed_tag_data.owner_mii.BuildFromStoreData(store_data);
// Admin info
settings.settings.amiibo_initialized.Assign(1);
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index aed12a7f8..f96d21220 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -6,7 +6,9 @@
#include <array>
#include "common/swap.h"
-#include "core/hle/service/mii/types.h"
+#include "core/hle/service/mii/types/char_info.h"
+#include "core/hle/service/mii/types/store_data.h"
+#include "core/hle/service/mii/types/ver3_store_data.h"
#include "core/hle/service/nfc/nfc_types.h"
namespace Service::NFP {
@@ -322,7 +324,7 @@ static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
// This is nn::nfp::RegisterInfoPrivate
struct RegisterInfoPrivate {
- Service::Mii::MiiStoreData mii_store_data;
+ Service::Mii::StoreData mii_store_data;
WriteDate creation_date;
AmiiboName amiibo_name;
u8 font_region;
diff --git a/src/core/hle/service/ngc/ngc.cpp b/src/core/hle/service/ngc/ngc.cpp
new file mode 100644
index 000000000..c26019ec0
--- /dev/null
+++ b/src/core/hle/service/ngc/ngc.cpp
@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/ngc/ngc.h"
+#include "core/hle/service/server_manager.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NGC {
+
+class NgctServiceImpl final : public ServiceFramework<NgctServiceImpl> {
+public:
+ explicit NgctServiceImpl(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &NgctServiceImpl::Match, "Match"},
+ {1, &NgctServiceImpl::Filter, "Filter"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Match(HLERequestContext& ctx) {
+ const auto buffer = ctx.ReadBuffer();
+ const auto text = Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(buffer.data()), buffer.size());
+
+ LOG_WARNING(Service_NGC, "(STUBBED) called, text={}", text);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ // Return false since we don't censor anything
+ rb.Push(false);
+ }
+
+ void Filter(HLERequestContext& ctx) {
+ const auto buffer = ctx.ReadBuffer();
+ const auto text = Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(buffer.data()), buffer.size());
+
+ LOG_WARNING(Service_NGC, "(STUBBED) called, text={}", text);
+
+ // Return the same string since we don't censor anything
+ ctx.WriteBuffer(buffer);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+};
+
+class NgcServiceImpl final : public ServiceFramework<NgcServiceImpl> {
+public:
+ explicit NgcServiceImpl(Core::System& system_) : ServiceFramework(system_, "ngc:u") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &NgcServiceImpl::GetContentVersion, "GetContentVersion"},
+ {1, &NgcServiceImpl::Check, "Check"},
+ {2, &NgcServiceImpl::Mask, "Mask"},
+ {3, &NgcServiceImpl::Reload, "Reload"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ static constexpr u32 NgcContentVersion = 1;
+
+ // This is nn::ngc::detail::ProfanityFilterOption
+ struct ProfanityFilterOption {
+ INSERT_PADDING_BYTES_NOINIT(0x20);
+ };
+ static_assert(sizeof(ProfanityFilterOption) == 0x20,
+ "ProfanityFilterOption has incorrect size");
+
+ void GetContentVersion(HLERequestContext& ctx) {
+ LOG_INFO(Service_NGC, "(STUBBED) called");
+
+ // This calls nn::ngc::ProfanityFilter::GetContentVersion
+ const u32 version = NgcContentVersion;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(version);
+ }
+
+ void Check(HLERequestContext& ctx) {
+ LOG_INFO(Service_NGC, "(STUBBED) called");
+
+ struct InputParameters {
+ u32 flags;
+ ProfanityFilterOption option;
+ };
+
+ IPC::RequestParser rp{ctx};
+ [[maybe_unused]] const auto params = rp.PopRaw<InputParameters>();
+ [[maybe_unused]] const auto input = ctx.ReadBuffer(0);
+
+ // This calls nn::ngc::ProfanityFilter::CheckProfanityWords
+ const u32 out_flags = 0;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(out_flags);
+ }
+
+ void Mask(HLERequestContext& ctx) {
+ LOG_INFO(Service_NGC, "(STUBBED) called");
+
+ struct InputParameters {
+ u32 flags;
+ ProfanityFilterOption option;
+ };
+
+ IPC::RequestParser rp{ctx};
+ [[maybe_unused]] const auto params = rp.PopRaw<InputParameters>();
+ const auto input = ctx.ReadBuffer(0);
+
+ // This calls nn::ngc::ProfanityFilter::MaskProfanityWordsInText
+ const u32 out_flags = 0;
+ ctx.WriteBuffer(input);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(out_flags);
+ }
+
+ void Reload(HLERequestContext& ctx) {
+ LOG_INFO(Service_NGC, "(STUBBED) called");
+
+ // This reloads the database.
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+};
+
+void LoopProcess(Core::System& system) {
+ auto server_manager = std::make_unique<ServerManager>(system);
+
+ server_manager->RegisterNamedService("ngct:u", std::make_shared<NgctServiceImpl>(system));
+ server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system));
+ ServerManager::RunServer(std::move(server_manager));
+}
+
+} // namespace Service::NGC
diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngc/ngc.h
index 27c34dad4..823b1aa81 100644
--- a/src/core/hle/service/ngct/ngct.h
+++ b/src/core/hle/service/ngc/ngc.h
@@ -7,8 +7,8 @@ namespace Core {
class System;
}
-namespace Service::NGCT {
+namespace Service::NGC {
void LoopProcess(Core::System& system);
-} // namespace Service::NGCT
+} // namespace Service::NGC
diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp
deleted file mode 100644
index 493c80ed2..000000000
--- a/src/core/hle/service/ngct/ngct.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/string_util.h"
-#include "core/core.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/ngct/ngct.h"
-#include "core/hle/service/server_manager.h"
-#include "core/hle/service/service.h"
-
-namespace Service::NGCT {
-
-class IService final : public ServiceFramework<IService> {
-public:
- explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IService::Match, "Match"},
- {1, &IService::Filter, "Filter"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void Match(HLERequestContext& ctx) {
- const auto buffer = ctx.ReadBuffer();
- const auto text = Common::StringFromFixedZeroTerminatedBuffer(
- reinterpret_cast<const char*>(buffer.data()), buffer.size());
-
- LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- // Return false since we don't censor anything
- rb.Push(false);
- }
-
- void Filter(HLERequestContext& ctx) {
- const auto buffer = ctx.ReadBuffer();
- const auto text = Common::StringFromFixedZeroTerminatedBuffer(
- reinterpret_cast<const char*>(buffer.data()), buffer.size());
-
- LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
-
- // Return the same string since we don't censor anything
- ctx.WriteBuffer(buffer);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-};
-
-void LoopProcess(Core::System& system) {
- auto server_manager = std::make_unique<ServerManager>(system);
-
- server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system));
- ServerManager::RunServer(std::move(server_manager));
-}
-
-} // namespace Service::NGCT
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 69cdb5918..0ad607391 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -43,7 +43,7 @@
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfp/nfp.h"
-#include "core/hle/service/ngct/ngct.h"
+#include "core/hle/service/ngc/ngc.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/npns/npns.h"
@@ -257,7 +257,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); });
- kernel.RunOnGuestCoreProcess("ngct", [&] { NGCT::LoopProcess(system); });
+ kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); });
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 5d28300e6..a983f23ea 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -48,15 +48,32 @@ enum class CallType {
using socklen_t = int;
+SOCKET interrupt_socket = static_cast<SOCKET>(-1);
+
+void InterruptSocketOperations() {
+ closesocket(interrupt_socket);
+}
+
+void AcknowledgeInterrupt() {
+ interrupt_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+}
+
void Initialize() {
WSADATA wsa_data;
(void)WSAStartup(MAKEWORD(2, 2), &wsa_data);
+
+ AcknowledgeInterrupt();
}
void Finalize() {
+ InterruptSocketOperations();
WSACleanup();
}
+SOCKET GetInterruptSocket() {
+ return interrupt_socket;
+}
+
sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result;
@@ -157,9 +174,42 @@ constexpr int SD_RECEIVE = SHUT_RD;
constexpr int SD_SEND = SHUT_WR;
constexpr int SD_BOTH = SHUT_RDWR;
-void Initialize() {}
+int interrupt_pipe_fd[2] = {-1, -1};
-void Finalize() {}
+void Initialize() {
+ if (pipe(interrupt_pipe_fd) != 0) {
+ LOG_ERROR(Network, "Failed to create interrupt pipe!");
+ }
+ int flags = fcntl(interrupt_pipe_fd[0], F_GETFL);
+ ASSERT_MSG(fcntl(interrupt_pipe_fd[0], F_SETFL, flags | O_NONBLOCK) == 0,
+ "Failed to set nonblocking state for interrupt pipe");
+}
+
+void Finalize() {
+ if (interrupt_pipe_fd[0] >= 0) {
+ close(interrupt_pipe_fd[0]);
+ }
+ if (interrupt_pipe_fd[1] >= 0) {
+ close(interrupt_pipe_fd[1]);
+ }
+}
+
+void InterruptSocketOperations() {
+ u8 value = 0;
+ ASSERT(write(interrupt_pipe_fd[1], &value, sizeof(value)) == 1);
+}
+
+void AcknowledgeInterrupt() {
+ u8 value = 0;
+ ssize_t ret = read(interrupt_pipe_fd[0], &value, sizeof(value));
+ if (ret != 1 && errno != EAGAIN && errno != EWOULDBLOCK) {
+ LOG_ERROR(Network, "Failed to acknowledge interrupt on shutdown");
+ }
+}
+
+SOCKET GetInterruptSocket() {
+ return interrupt_pipe_fd[0];
+}
sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result;
@@ -490,6 +540,14 @@ NetworkInstance::~NetworkInstance() {
Finalize();
}
+void CancelPendingSocketOperations() {
+ InterruptSocketOperations();
+}
+
+void RestartSocketOperations() {
+ AcknowledgeInterrupt();
+}
+
std::optional<IPv4Address> GetHostIPv4Address() {
const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface.has_value()) {
@@ -560,7 +618,14 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
return result;
});
- const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout);
+ host_pollfds.push_back(WSAPOLLFD{
+ .fd = GetInterruptSocket(),
+ .events = POLLIN,
+ .revents = 0,
+ });
+
+ const int result =
+ WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), timeout);
if (result == 0) {
ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(),
[](WSAPOLLFD fd) { return fd.revents == 0; }));
@@ -627,6 +692,24 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
+
+ std::vector<WSAPOLLFD> host_pollfds{
+ WSAPOLLFD{fd, POLLIN, 0},
+ WSAPOLLFD{GetInterruptSocket(), POLLIN, 0},
+ };
+
+ while (true) {
+ const int pollres =
+ WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1);
+ if (host_pollfds[1].revents != 0) {
+ // Interrupt signaled before a client could be accepted, break
+ return {AcceptResult{}, Errno::AGAIN};
+ }
+ if (pollres > 0) {
+ break;
+ }
+ }
+
const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen);
if (new_socket == INVALID_SOCKET) {
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index c7e20ae34..b7b7d773a 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -96,6 +96,9 @@ public:
~NetworkInstance();
};
+void CancelPendingSocketOperations();
+void RestartSocketOperations();
+
#ifdef _WIN32
constexpr IPv4Address TranslateIPv4(in_addr addr) {
auto& bytes = addr.S_un.S_un_b;
diff --git a/src/core/tools/renderdoc.cpp b/src/core/tools/renderdoc.cpp
new file mode 100644
index 000000000..44d24822a
--- /dev/null
+++ b/src/core/tools/renderdoc.cpp
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <renderdoc_app.h>
+
+#include "common/assert.h"
+#include "common/dynamic_library.h"
+#include "core/tools/renderdoc.h"
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#endif
+
+namespace Tools {
+
+RenderdocAPI::RenderdocAPI() {
+#ifdef WIN32
+ if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) {
+ const auto RENDERDOC_GetAPI =
+ reinterpret_cast<pRENDERDOC_GetAPI>(GetProcAddress(mod, "RENDERDOC_GetAPI"));
+ const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api);
+ ASSERT(ret == 1);
+ }
+#else
+#ifdef ANDROID
+ static constexpr const char RENDERDOC_LIB[] = "libVkLayer_GLES_RenderDoc.so";
+#else
+ static constexpr const char RENDERDOC_LIB[] = "librenderdoc.so";
+#endif
+ if (void* mod = dlopen(RENDERDOC_LIB, RTLD_NOW | RTLD_NOLOAD)) {
+ const auto RENDERDOC_GetAPI =
+ reinterpret_cast<pRENDERDOC_GetAPI>(dlsym(mod, "RENDERDOC_GetAPI"));
+ const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api);
+ ASSERT(ret == 1);
+ }
+#endif
+}
+
+RenderdocAPI::~RenderdocAPI() = default;
+
+void RenderdocAPI::ToggleCapture() {
+ if (!rdoc_api) [[unlikely]] {
+ return;
+ }
+ if (!is_capturing) {
+ rdoc_api->StartFrameCapture(NULL, NULL);
+ } else {
+ rdoc_api->EndFrameCapture(NULL, NULL);
+ }
+ is_capturing = !is_capturing;
+}
+
+} // namespace Tools
diff --git a/src/core/tools/renderdoc.h b/src/core/tools/renderdoc.h
new file mode 100644
index 000000000..0e5e43da5
--- /dev/null
+++ b/src/core/tools/renderdoc.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+struct RENDERDOC_API_1_6_0;
+
+namespace Tools {
+
+class RenderdocAPI {
+public:
+ explicit RenderdocAPI();
+ ~RenderdocAPI();
+
+ void ToggleCapture();
+
+private:
+ RENDERDOC_API_1_6_0* rdoc_api{};
+ bool is_capturing{false};
+};
+
+} // namespace Tools
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 34240b36f..8decdf399 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -204,9 +204,7 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
if (def.count > 1) {
throw NotImplementedException("Indirect texture sample");
}
- const Id sampler_id{def.id};
- const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)};
- return ctx.OpImage(ctx.image_buffer_type, id);
+ return ctx.OpLoad(ctx.image_buffer_type, def.id);
} else {
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
if (def.count > 1) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 238fb40e3..72f69b7aa 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1247,9 +1247,8 @@ void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) {
}
const spv::ImageFormat format{spv::ImageFormat::Unknown};
image_buffer_type = TypeImage(F32[1], spv::Dim::Buffer, 0U, false, false, 1, format);
- sampled_texture_buffer_type = TypeSampledImage(image_buffer_type);
- const Id type{TypePointer(spv::StorageClass::UniformConstant, sampled_texture_buffer_type)};
+ const Id type{TypePointer(spv::StorageClass::UniformConstant, image_buffer_type)};
texture_buffers.reserve(info.texture_buffer_descriptors.size());
for (const TextureBufferDescriptor& desc : info.texture_buffer_descriptors) {
if (desc.count != 1) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index e63330f11..7c49fd504 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -206,7 +206,6 @@ public:
Id output_u32{};
Id image_buffer_type{};
- Id sampled_texture_buffer_type{};
Id image_u32{};
std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 4457b366f..1bdb0def5 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -719,6 +719,7 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_ad
return nullptr;
}
const auto& image_map_ids = it->second;
+ boost::container::small_vector<const ImageBase*, 4> valid_images;
for (const ImageMapId map_id : image_map_ids) {
const ImageMapView& map = slot_map_views[map_id];
const ImageBase& image = slot_images[map.image_id];
@@ -728,8 +729,20 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_ad
if (image.image_view_ids.empty()) {
continue;
}
- return &slot_image_views[image.image_view_ids.at(0)];
+ valid_images.push_back(&image);
}
+
+ if (valid_images.size() == 1) [[likely]] {
+ return &slot_image_views[valid_images[0]->image_view_ids.at(0)];
+ }
+
+ if (valid_images.size() > 0) [[unlikely]] {
+ std::ranges::sort(valid_images, [](const auto* a, const auto* b) {
+ return a->modification_tick > b->modification_tick;
+ });
+ return &slot_image_views[valid_images[0]->image_view_ids.at(0)];
+ }
+
return nullptr;
}
diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp
index 4988fcc83..b457a736a 100644
--- a/src/yuzu/applets/qt_amiibo_settings.cpp
+++ b/src/yuzu/applets/qt_amiibo_settings.cpp
@@ -160,7 +160,8 @@ void QtAmiiboSettingsDialog::LoadAmiiboData() {
}
const auto amiibo_name = std::string(register_info.amiibo_name.data());
- const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data());
+ const auto owner_name =
+ Common::UTF16ToUTF8(register_info.mii_char_info.GetNickname().data.data());
const auto creation_date =
QDate(register_info.creation_date.year, register_info.creation_date.month,
register_info.creation_date.day);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index cbeb8f168..b22fda746 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -59,6 +59,8 @@ void ConfigureDebug::SetConfiguration() {
ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
+ ui->enable_renderdoc_hotkey->setEnabled(runtime_lock);
+ ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -111,6 +113,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
+ Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked();
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 97c7d9022..66b8b7459 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -18,8 +18,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>829</width>
- <height>758</height>
+ <width>842</width>
+ <height>741</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_1">
@@ -260,7 +260,7 @@
<string>Graphics</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
- <item row="3" column="0">
+ <item row="4" column="0">
<widget class="QCheckBox" name="disable_loop_safety_checks">
<property name="toolTip">
<string>When checked, it executes shaders without loop logic changes</string>
@@ -270,33 +270,53 @@
</property>
</widget>
</item>
- <item row="4" column="0">
- <widget class="QCheckBox" name="dump_shaders">
+ <item row="8" column="0">
+ <widget class="QCheckBox" name="disable_macro_hle">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
- <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
+ <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
</property>
<property name="text">
- <string>Dump Game Shaders</string>
+ <string>Disable Macro HLE</string>
</property>
</widget>
</item>
<item row="7" column="0">
- <widget class="QCheckBox" name="disable_macro_hle">
+ <widget class="QCheckBox" name="dump_macros">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
- <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
+ <string>When checked, it will dump all the macro programs of the GPU</string>
</property>
<property name="text">
- <string>Disable Macro HLE</string>
+ <string>Dump Maxwell Macros</string>
</property>
</widget>
</item>
- <item row="5" column="0">
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="enable_nsight_aftermath">
+ <property name="toolTip">
+ <string>When checked, it enables Nsight Aftermath crash dumps</string>
+ </property>
+ <property name="text">
+ <string>Enable Nsight Aftermath</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="enable_shader_feedback">
+ <property name="toolTip">
+ <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
+ </property>
+ <property name="text">
+ <string>Enable Shader Feedback</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
@@ -309,6 +329,22 @@
</property>
</widget>
</item>
+ <item row="9" column="0">
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
<item row="0" column="0">
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
@@ -322,55 +358,26 @@
</property>
</widget>
</item>
- <item row="6" column="0">
- <widget class="QCheckBox" name="dump_macros">
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="dump_shaders">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
- <string>When checked, it will dump all the macro programs of the GPU</string>
+ <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
</property>
<property name="text">
- <string>Dump Maxwell Macros</string>
+ <string>Dump Game Shaders</string>
</property>
</widget>
</item>
<item row="1" column="0">
- <widget class="QCheckBox" name="enable_shader_feedback">
- <property name="toolTip">
- <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
- </property>
- <property name="text">
- <string>Enable Shader Feedback</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="enable_nsight_aftermath">
- <property name="toolTip">
- <string>When checked, it enables Nsight Aftermath crash dumps</string>
- </property>
+ <widget class="QCheckBox" name="enable_renderdoc_hotkey">
<property name="text">
- <string>Enable Nsight Aftermath</string>
+ <string>Enable Renderdoc Hotkey</string>
</property>
</widget>
</item>
- <item row="8" column="0">
- <spacer name="verticalSpacer_5">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Preferred</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
</item>
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 6842ced3e..f254c1e1c 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -589,10 +589,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
});
connect(start_game, &QAction::triggered, [this, path]() {
- emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal);
+ emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal,
+ AmLaunchType::UserInitiated);
});
connect(start_game_global, &QAction::triggered, [this, path]() {
- emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global);
+ emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global,
+ AmLaunchType::UserInitiated);
});
connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 8aea646b2..1fcbbf0ba 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -28,6 +28,7 @@ class GameListWorker;
class GameListSearchField;
class GameListDir;
class GMainWindow;
+enum class AmLaunchType;
enum class StartGameType;
namespace FileSys {
@@ -103,7 +104,7 @@ public:
signals:
void BootGame(const QString& game_path, u64 program_id, std::size_t program_index,
- StartGameType type);
+ StartGameType type, AmLaunchType launch_type);
void GameChosen(const QString& game_path, const u64 title_id = 0);
void ShouldCancelWorker();
void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index 848239c35..56eee8d82 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -4,10 +4,12 @@
#pragma once
#include <map>
+#include <QKeySequence>
+#include <QString>
+#include <QWidget>
#include "core/hid/hid_types.h"
class QDialog;
-class QKeySequence;
class QSettings;
class QShortcut;
class ControllerShortcut;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9cea60c32..d32aa9615 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,6 +8,8 @@
#include <iostream>
#include <memory>
#include <thread>
+#include "core/loader/nca.h"
+#include "core/tools/renderdoc.h"
#ifdef __APPLE__
#include <unistd.h> // for chdir
#endif
@@ -1347,6 +1349,11 @@ void GMainWindow::InitializeHotkeys() {
connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
});
+ connect_shortcut(QStringLiteral("Toggle Renderdoc Capture"), [this] {
+ if (Settings::values.enable_renderdoc_hotkey) {
+ system->GetRenderdocAPI().ToggleCapture();
+ }
+ });
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
if (Settings::values.mouse_enabled) {
Settings::values.mouse_panning = false;
@@ -1554,6 +1561,7 @@ void GMainWindow::ConnectMenuEvents() {
// Help
connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
+ connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
connect_menu(ui->action_About, &GMainWindow::OnAbout);
}
@@ -1705,7 +1713,8 @@ void GMainWindow::AllowOSSleep() {
#endif
}
-bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
+bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index,
+ AmLaunchType launch_type) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr) {
ShutdownGame();
@@ -1717,6 +1726,10 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
system->SetFilesystem(vfs);
+ if (launch_type == AmLaunchType::UserInitiated) {
+ system->GetUserChannel().clear();
+ }
+
system->SetAppletFrontendSet({
std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
(UISettings::values.controller_applet_disabled.GetValue() == true)
@@ -1856,7 +1869,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
}
void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
- StartGameType type) {
+ StartGameType type, AmLaunchType launch_type) {
LOG_INFO(Frontend, "yuzu starting...");
StoreRecentFile(filename); // Put the filename on top of the list
@@ -1900,7 +1913,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
}
}
- if (!LoadROM(filename, program_id, program_index)) {
+ if (!LoadROM(filename, program_id, program_index, launch_type)) {
return;
}
@@ -3369,7 +3382,8 @@ void GMainWindow::OnLoadComplete() {
void GMainWindow::OnExecuteProgram(std::size_t program_index) {
ShutdownGame();
- BootGame(last_filename_booted, 0, program_index);
+ BootGame(last_filename_booted, 0, program_index, StartGameType::Normal,
+ AmLaunchType::ApplicationInitiated);
}
void GMainWindow::OnExit() {
@@ -4000,6 +4014,108 @@ void GMainWindow::OnOpenYuzuFolder() {
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir))));
}
+void GMainWindow::OnVerifyInstalledContents() {
+ // Declare sizes.
+ size_t total_size = 0;
+ size_t processed_size = 0;
+
+ // Initialize a progress dialog.
+ QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setMinimumDuration(100);
+ progress.setAutoClose(false);
+ progress.setAutoReset(false);
+
+ // Declare a list of file names which failed to verify.
+ std::vector<std::string> failed;
+
+ // Declare progress callback.
+ auto QtProgressCallback = [&](size_t nca_processed, size_t nca_total) {
+ if (progress.wasCanceled()) {
+ return false;
+ }
+ progress.setValue(static_cast<int>(((processed_size + nca_processed) * 100) / total_size));
+ return true;
+ };
+
+ // Get content registries.
+ auto bis_contents = system->GetFileSystemController().GetSystemNANDContents();
+ auto user_contents = system->GetFileSystemController().GetUserNANDContents();
+
+ std::vector<FileSys::RegisteredCache*> content_providers;
+ if (bis_contents) {
+ content_providers.push_back(bis_contents);
+ }
+ if (user_contents) {
+ content_providers.push_back(user_contents);
+ }
+
+ // Get associated NCA files.
+ std::vector<FileSys::VirtualFile> nca_files;
+
+ // Get all installed IDs.
+ for (auto nca_provider : content_providers) {
+ const auto entries = nca_provider->ListEntriesFilter();
+
+ for (const auto& entry : entries) {
+ auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type);
+ if (!nca_file) {
+ continue;
+ }
+
+ total_size += nca_file->GetSize();
+ nca_files.push_back(std::move(nca_file));
+ }
+ }
+
+ // Using the NCA loader, determine if all NCAs are valid.
+ for (auto& nca_file : nca_files) {
+ Loader::AppLoader_NCA nca_loader(nca_file);
+
+ auto status = nca_loader.VerifyIntegrity(QtProgressCallback);
+ if (progress.wasCanceled()) {
+ break;
+ }
+ if (status != Loader::ResultStatus::Success) {
+ FileSys::NCA nca(nca_file);
+ const auto title_id = nca.GetTitleId();
+ std::string title_name = "unknown";
+
+ const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
+ FileSys::ContentRecordType::Control);
+ if (control && control->GetStatus() == Loader::ResultStatus::Success) {
+ const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
+ *provider};
+ const auto [nacp, logo] = pm.ParseControlNCA(*control);
+ if (nacp) {
+ title_name = nacp->GetApplicationName();
+ }
+ }
+
+ if (title_id > 0) {
+ failed.push_back(
+ fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name));
+ } else {
+ failed.push_back(fmt::format("{} (unknown)", nca_file->GetName()));
+ }
+ }
+
+ processed_size += nca_file->GetSize();
+ }
+
+ progress.close();
+
+ if (failed.size() > 0) {
+ auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(failed, "\n")));
+ QMessageBox::critical(
+ this, tr("Integrity verification failed!"),
+ tr("Verification failed for the following files:\n\n%1").arg(failed_names));
+ } else {
+ QMessageBox::information(this, tr("Integrity verification succeeded!"),
+ tr("The operation completed successfully."));
+ }
+}
+
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 1e4f6e477..cf191f698 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -58,6 +58,11 @@ enum class StartGameType {
Global, // Only uses global configuration
};
+enum class AmLaunchType {
+ UserInitiated,
+ ApplicationInitiated,
+};
+
namespace Core {
enum class SystemResultStatus : u32;
class System;
@@ -239,9 +244,11 @@ private:
void PreventOSSleep();
void AllowOSSleep();
- bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index);
+ bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index,
+ AmLaunchType launch_type);
void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0,
- StartGameType with_config = StartGameType::Normal);
+ StartGameType with_config = StartGameType::Normal,
+ AmLaunchType launch_type = AmLaunchType::UserInitiated);
void ShutdownGame();
void ShowTelemetryCallout();
@@ -343,6 +350,7 @@ private slots:
void OnConfigurePerGame();
void OnLoadAmiibo();
void OnOpenYuzuFolder();
+ void OnVerifyInstalledContents();
void OnAbout();
void OnToggleFilterBar();
void OnToggleStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 013ba0ceb..e54d7d75d 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -148,6 +148,7 @@
<addaction name="action_Configure_Tas"/>
</widget>
<addaction name="action_Rederive"/>
+ <addaction name="action_Verify_installed_contents"/>
<addaction name="separator"/>
<addaction name="action_Capture_Screenshot"/>
<addaction name="menuTAS"/>
@@ -214,6 +215,11 @@
<string>&amp;Reinitialize keys...</string>
</property>
</action>
+ <action name="action_Verify_installed_contents">
+ <property name="text">
+ <string>Verify installed contents</string>
+ </property>
+ </action>
<action name="action_About">
<property name="text">
<string>&amp;About yuzu</string>
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c1695cc6e..55d0938f7 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -358,6 +358,7 @@ int main(int argc, char** argv) {
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
+ system.GetUserChannel().clear();
const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)};