From 531572b411a4a311cb38bcf09a2c95559ef068aa Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 26 Aug 2023 18:18:13 -0400 Subject: [PATCH 01/69] internal_network: cancel pending socket operations on application process termination --- src/core/core.cpp | 2 + src/core/internal_network/network.cpp | 86 ++++++++++++++++++++++++++- src/core/internal_network/network.h | 3 + 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 2f67e60a97..9c5246a563 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -404,6 +404,7 @@ struct System::Impl { gpu_core->NotifyShutdown(); } + Network::CancelPendingSocketOperations(); kernel.SuspendApplication(true); if (services) { services->KillNVNFlinger(); @@ -425,6 +426,7 @@ struct System::Impl { debugger.reset(); kernel.Shutdown(); memory.Reset(); + Network::RestartSocketOperations(); if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info{}; diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 5d28300e6e..ef5e5d0135 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(-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,39 @@ 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; + read(interrupt_pipe_fd[0], &value, sizeof(value)); +} + +SOCKET GetInterruptSocket() { + return interrupt_pipe_fd[0]; +} sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -490,6 +537,14 @@ NetworkInstance::~NetworkInstance() { Finalize(); } +void CancelPendingSocketOperations() { + InterruptSocketOperations(); +} + +void RestartSocketOperations() { + AcknowledgeInterrupt(); +} + std::optional GetHostIPv4Address() { const auto network_interface = Network::GetSelectedNetworkInterface(); if (!network_interface.has_value()) { @@ -560,7 +615,14 @@ std::pair Poll(std::vector& pollfds, s32 timeout) { return result; }); - const int result = WSAPoll(host_pollfds.data(), static_cast(num), timeout); + host_pollfds.push_back(WSAPOLLFD{ + .fd = GetInterruptSocket(), + .events = POLLIN, + .revents = 0, + }); + + const int result = + WSAPoll(host_pollfds.data(), static_cast(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 +689,24 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { std::pair Socket::Accept() { sockaddr_in addr; socklen_t addrlen = sizeof(addr); + + std::vector host_pollfds{ + WSAPOLLFD{fd, POLLIN, 0}, + WSAPOLLFD{GetInterruptSocket(), POLLIN, 0}, + }; + + while (true) { + const int pollres = + WSAPoll(host_pollfds.data(), static_cast(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(&addr), &addrlen); if (new_socket == INVALID_SOCKET) { diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index c7e20ae348..b7b7d773a4 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; From bdd09d684457319d8ab1219fd50769550d1dc145 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 2 Sep 2023 11:43:16 -0400 Subject: [PATCH 02/69] polyfill_thread: ensure mutex was locked before signaling stop --- src/common/polyfill_thread.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h index b5ef055dba..41cbb9ed5f 100644 --- a/src/common/polyfill_thread.h +++ b/src/common/polyfill_thread.h @@ -19,8 +19,8 @@ namespace Common { template -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& lk, std::stop_token token, Pred&& pred) { + cv.wait(lk, token, std::move(pred)); } template @@ -332,13 +332,17 @@ private: namespace Common { template -void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) { +void CondvarWait(Condvar& cv, std::unique_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 @@ -353,8 +357,10 @@ bool StoppableTimedWait(std::stop_token token, const std::chrono::duration Date: Sat, 2 Sep 2023 17:25:00 -0400 Subject: [PATCH 03/69] shader_recompiler: always declare image format for image buffers --- src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index bec5db1735..238fb40e3d 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -74,6 +74,11 @@ spv::ImageFormat GetImageFormat(ImageFormat format) { throw InvalidArgument("Invalid image format {}", format); } +spv::ImageFormat GetImageFormatForBuffer(ImageFormat format) { + const auto spv_format = GetImageFormat(format); + return spv_format == spv::ImageFormat::Unknown ? spv::ImageFormat::R32ui : spv_format; +} + Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { const spv::ImageFormat format{GetImageFormat(desc.format)}; const Id type{ctx.U32[1]}; @@ -1271,7 +1276,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) { if (desc.count != 1) { throw NotImplementedException("Array of image buffers"); } - const spv::ImageFormat format{GetImageFormat(desc.format)}; + const spv::ImageFormat format{GetImageFormatForBuffer(desc.format)}; const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; From ea46efd9a2713d28936141066e3d4418f28c4648 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:21:14 -0400 Subject: [PATCH 04/69] configure_graphics: Fix handling of broken Vulkan The VSync combobox wouldn't populate if there was no Vulkan device, which caused issues with trying to set VSync on other backends. This also adds another layer to GetCurrentGraphicsBackend to check for broken Vulkan and return OpenGL instead of Vulkan. --- src/yuzu/configuration/configure_graphics.cpp | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 8622dc184d..f36a0cae78 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -193,14 +193,10 @@ void ConfigureGraphics::PopulateVSyncModeSelection() { : vsync_mode_combobox_enum_map[current_index]; int index{}; const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device - if (device == -1) { - // Invalid device - return; - } const auto& present_modes = //< relevant vector of present modes for the selected device or API - backend == Settings::RendererBackend::Vulkan ? device_present_modes[device] - : default_present_modes; + backend == Settings::RendererBackend::Vulkan && device > -1 ? device_present_modes[device] + : default_present_modes; vsync_mode_combobox->clear(); vsync_mode_combobox_enum_map.clear(); @@ -497,11 +493,19 @@ void ConfigureGraphics::RetrieveVulkanDevices() { } Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { - if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { - return Settings::values.renderer_backend.GetValue(true); + const auto selected_backend = [=]() { + if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { + return Settings::values.renderer_backend.GetValue(true); + } + return static_cast( + combobox_translations.at(Settings::EnumMetadata::Index()) + .at(api_combobox->currentIndex()) + .first); + }(); + + if (selected_backend == Settings::RendererBackend::Vulkan && + UISettings::values.has_broken_vulkan) { + return Settings::RendererBackend::OpenGL; } - return static_cast( - combobox_translations.at(Settings::EnumMetadata::Index()) - .at(api_combobox->currentIndex()) - .first); + return selected_backend; } From d078cff269b372106bd5e62dc4e25dd4694e33b4 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:50:21 -0400 Subject: [PATCH 05/69] configure_graphics: Capture by reference Small optimization. --- src/yuzu/configuration/configure_graphics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index f36a0cae78..fd6bebf0f3 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -493,7 +493,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() { } Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { - const auto selected_backend = [=]() { + const auto selected_backend = [&]() { if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { return Settings::values.renderer_backend.GetValue(true); } From e4ebabcd5bde463d4534fbc41538af77495508e6 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:59:10 -0400 Subject: [PATCH 06/69] yuzu-qt: Update API Text for broken Vulkan Otherwise caused a blue Vulkan badge to appear in the status bar. --- src/yuzu/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f2e6c03f0d..91c7797cf5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -444,6 +444,7 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; + UpdateAPIText(); renderer_status_button->setDisabled(true); renderer_status_button->setChecked(false); } else { From d8943e5bac380bbf49e1064aa30a9ec6b8f9be91 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:59:44 -0400 Subject: [PATCH 07/69] yuzu-qt: Use Null when OpenGL is not compiled --- src/yuzu/main.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 91c7797cf5..5262769bbc 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -442,7 +442,11 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>" "here for instructions to fix the issue.")); +#ifdef HAS_OPENGL Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; +#else + Settings::values.renderer_backend = Settings::RendererBackend::Null; +#endif UpdateAPIText(); renderer_status_button->setDisabled(true); @@ -3771,10 +3775,14 @@ void GMainWindow::OnToggleAdaptingFilter() { void GMainWindow::OnToggleGraphicsAPI() { auto api = Settings::values.renderer_backend.GetValue(); - if (api == Settings::RendererBackend::OpenGL) { + if (api != Settings::RendererBackend::Vulkan) { api = Settings::RendererBackend::Vulkan; } else { +#ifdef HAS_OPENGL api = Settings::RendererBackend::OpenGL; +#else + api = Settings::RendererBackend::Null; +#endif } Settings::values.renderer_backend.SetValue(api); renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan); From 716e0a126a22cfdeeaad6204f236324429345d2e Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 6 Sep 2023 01:06:03 -0400 Subject: [PATCH 08/69] core: implement basic integrity verification --- src/core/loader/loader.cpp | 4 +- src/core/loader/loader.h | 10 +++++ src/core/loader/nca.cpp | 76 ++++++++++++++++++++++++++++++++++++++ src/core/loader/nca.h | 2 + src/core/loader/nsp.cpp | 36 ++++++++++++++++++ src/core/loader/nsp.h | 2 + src/core/loader/xci.cpp | 34 +++++++++++++++++ src/core/loader/xci.h | 2 + src/yuzu/game_list.cpp | 3 ++ src/yuzu/game_list.h | 1 + src/yuzu/main.cpp | 50 +++++++++++++++++++++++++ src/yuzu/main.h | 1 + 12 files changed, 220 insertions(+), 1 deletion(-) diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 07c65dc1a2..b6e355622e 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -108,7 +108,7 @@ std::string GetFileTypeString(FileType type) { return "unknown"; } -constexpr std::array RESULT_MESSAGES{ +constexpr std::array RESULT_MESSAGES{ "The operation completed successfully.", "The loader requested to load is already loaded.", "The operation is not implemented.", @@ -175,6 +175,8 @@ constexpr std::array RESULT_MESSAGES{ "The KIP BLZ decompression of the section failed unexpectedly.", "The INI file has a bad header.", "The INI file contains more than the maximum allowable number of KIP files.", + "Integrity verification could not be performed for this file.", + "Integrity verification failed.", }; std::string GetResultStatusString(ResultStatus status) { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 721eb8e8c7..b4828f7cd1 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -132,6 +133,8 @@ enum class ResultStatus : u16 { ErrorBLZDecompressionFailed, ErrorBadINIHeader, ErrorINITooManyKIPs, + ErrorIntegrityVerificationNotImplemented, + ErrorIntegrityVerificationFailed, }; std::string GetResultStatusString(ResultStatus status); @@ -169,6 +172,13 @@ public: */ virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0; + /** + * Try to verify the integrity of the file. + */ + virtual ResultStatus VerifyIntegrity(std::function progress_callback) { + return ResultStatus::ErrorIntegrityVerificationNotImplemented; + } + /** * Get the code (typically .code section) of the application * diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 09d40e695d..4feb6968ae 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -3,6 +3,8 @@ #include +#include "common/hex_util.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" @@ -12,6 +14,7 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/deconstructed_rom_directory.h" #include "core/loader/nca.h" +#include "mbedtls/sha256.h" namespace Loader { @@ -80,6 +83,79 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S return load_result; } +ResultStatus AppLoader_NCA::VerifyIntegrity(std::function progress_callback) { + using namespace Common::Literals; + + constexpr size_t NcaFileNameWithHashLength = 36; + constexpr size_t NcaFileNameHashLength = 32; + constexpr size_t NcaSha256HashLength = 32; + constexpr size_t NcaSha256HalfHashLength = NcaSha256HashLength / 2; + + // Get the file name. + const auto name = file->GetName(); + + // We won't try to verify meta NCAs. + if (name.ends_with(".cnmt.nca")) { + return ResultStatus::Success; + } + + // Check if we can verify this file. NCAs should be named after their hashes. + if (!name.ends_with(".nca") || name.size() != NcaFileNameWithHashLength) { + LOG_WARNING(Loader, "Unable to validate NCA with name {}", name); + return ResultStatus::ErrorIntegrityVerificationNotImplemented; + } + + // Get the expected truncated hash of the NCA. + const auto input_hash = + Common::HexStringToVector(file->GetName().substr(0, NcaFileNameHashLength), false); + + // Declare buffer to read into. + std::vector buffer(4_MiB); + + // Initialize sha256 verification context. + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + mbedtls_sha256_starts_ret(&ctx, 0); + + // Ensure we maintain a clean state on exit. + SCOPE_EXIT({ mbedtls_sha256_free(&ctx); }); + + // Declare counters. + const size_t total_size = file->GetSize(); + size_t processed_size = 0; + + // Begin iterating the file. + while (processed_size < total_size) { + // Refill the buffer. + const size_t intended_read_size = std::min(buffer.size(), total_size - processed_size); + const size_t read_size = file->Read(buffer.data(), intended_read_size, processed_size); + + // Update the hash function with the buffer contents. + mbedtls_sha256_update_ret(&ctx, buffer.data(), read_size); + + // Update counters. + processed_size += read_size; + + // Call the progress function. + if (!progress_callback(processed_size, total_size)) { + return ResultStatus::ErrorIntegrityVerificationFailed; + } + } + + // Finalize context and compute the output hash. + std::array output_hash; + mbedtls_sha256_finish_ret(&ctx, output_hash.data()); + + // Compare to expected. + if (std::memcmp(input_hash.data(), output_hash.data(), NcaSha256HalfHashLength) != 0) { + LOG_ERROR(Loader, "NCA hash mismatch detected for file {}", name); + return ResultStatus::ErrorIntegrityVerificationFailed; + } + + // File verified. + return ResultStatus::Success; +} + ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { if (nca == nullptr) { return ResultStatus::ErrorNotInitialized; diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index cf356ce63b..96779e27fc 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -39,6 +39,8 @@ public: LoadResult Load(Kernel::KProcess& process, Core::System& system) override; + ResultStatus VerifyIntegrity(std::function progress_callback) override; + ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index f9b2549a32..fe2af1ae6f 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -117,6 +117,42 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S return result; } +ResultStatus AppLoader_NSP::VerifyIntegrity(std::function progress_callback) { + // Extracted-type NSPs can't be verified. + if (nsp->IsExtractedType()) { + return ResultStatus::ErrorIntegrityVerificationNotImplemented; + } + + // Get list of all NCAs. + const auto ncas = nsp->GetNCAsCollapsed(); + + size_t total_size = 0; + size_t processed_size = 0; + + // Loop over NCAs, collecting the total size to verify. + for (const auto& nca : ncas) { + total_size += nca->GetBaseFile()->GetSize(); + } + + // Loop over NCAs again, verifying each. + for (const auto& nca : ncas) { + AppLoader_NCA loader_nca(nca->GetBaseFile()); + + const auto NcaProgressCallback = [&](size_t nca_processed_size, size_t nca_total_size) { + return progress_callback(processed_size + nca_processed_size, total_size); + }; + + const auto verification_result = loader_nca.VerifyIntegrity(NcaProgressCallback); + if (verification_result != ResultStatus::Success) { + return verification_result; + } + + processed_size += nca->GetBaseFile()->GetSize(); + } + + return ResultStatus::Success; +} + ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) { return secondary_loader->ReadRomFS(out_file); } diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 79df4586ac..7ce436c67e 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -45,6 +45,8 @@ public: LoadResult Load(Kernel::KProcess& process, Core::System& system) override; + ResultStatus VerifyIntegrity(std::function progress_callback) override; + ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 3a76bc7888..12d72c3809 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -85,6 +85,40 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S return result; } +ResultStatus AppLoader_XCI::VerifyIntegrity(std::function progress_callback) { + // Verify secure partition, as it is the only thing we can process. + auto secure_partition = xci->GetSecurePartitionNSP(); + + // Get list of all NCAs. + const auto ncas = secure_partition->GetNCAsCollapsed(); + + size_t total_size = 0; + size_t processed_size = 0; + + // Loop over NCAs, collecting the total size to verify. + for (const auto& nca : ncas) { + total_size += nca->GetBaseFile()->GetSize(); + } + + // Loop over NCAs again, verifying each. + for (const auto& nca : ncas) { + AppLoader_NCA loader_nca(nca->GetBaseFile()); + + const auto NcaProgressCallback = [&](size_t nca_processed_size, size_t nca_total_size) { + return progress_callback(processed_size + nca_processed_size, total_size); + }; + + const auto verification_result = loader_nca.VerifyIntegrity(NcaProgressCallback); + if (verification_result != ResultStatus::Success) { + return verification_result; + } + + processed_size += nca->GetBaseFile()->GetSize(); + } + + return ResultStatus::Success; +} + ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& out_file) { return nca_loader->ReadRomFS(out_file); } diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index ff05e6f628..b02e136d3d 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -45,6 +45,8 @@ public: LoadResult Load(Kernel::KProcess& process, Core::System& system) override; + ResultStatus VerifyIntegrity(std::function progress_callback) override; + ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b5a02700d5..6842ced3e3 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -557,6 +557,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); + QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); #ifndef WIN32 @@ -628,6 +629,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() { emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC); }); + connect(verify_integrity, &QAction::triggered, + [this, path]() { emit VerifyIntegrityRequested(path); }); connect(copy_tid, &QAction::triggered, [this, program_id]() { emit CopyTIDRequested(program_id); }); connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 6c2f75e53e..8aea646b24 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -113,6 +113,7 @@ signals: void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, const std::string& game_path); void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); + void VerifyIntegrityRequested(const std::string& game_path); void CopyTIDRequested(u64 program_id); void CreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f2e6c03f0d..0bef1ebfef 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1447,6 +1447,8 @@ void GMainWindow::ConnectWidgetEvents() { &GMainWindow::OnGameListRemoveInstalledEntry); connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); + connect(game_list, &GameList::VerifyIntegrityRequested, this, + &GMainWindow::OnGameListVerifyIntegrity); connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, &GMainWindow::OnGameListNavigateToGamedbEntry); @@ -2684,6 +2686,54 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa } } +void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) { + const auto NotImplemented = [this] { + QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"), + tr("File contents were not checked for validity.")); + }; + const auto Failed = [this] { + QMessageBox::critical(this, tr("Integrity verification failed!"), + tr("File contents may be corrupt.")); + }; + + const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + if (loader == nullptr) { + NotImplemented(); + return; + } + + QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); + progress.setWindowModality(Qt::WindowModal); + progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + + const auto QtProgressCallback = [&](size_t processed_size, size_t total_size) { + if (progress.wasCanceled()) { + return false; + } + + progress.setValue(static_cast((processed_size * 100) / total_size)); + return true; + }; + + const auto status = loader->VerifyIntegrity(QtProgressCallback); + if (progress.wasCanceled() || + status == Loader::ResultStatus::ErrorIntegrityVerificationNotImplemented) { + NotImplemented(); + return; + } + + if (status == Loader::ResultStatus::ErrorIntegrityVerificationFailed) { + Failed(); + return; + } + + progress.close(); + QMessageBox::information(this, tr("Integrity verification succeeded!"), + tr("The operation completed successfully.")); +} + void GMainWindow::OnGameListCopyTID(u64 program_id) { QClipboard* clipboard = QGuiApplication::clipboard(); clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id))); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 668dbc3b13..1e4f6e477e 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -313,6 +313,7 @@ private slots: void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, const std::string& game_path); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); + void OnGameListVerifyIntegrity(const std::string& game_path); void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); From a02d641042d031999f3b4b90a2b5190417e051c5 Mon Sep 17 00:00:00 2001 From: xcfrg <30675315+xcfrg@users.noreply.github.com> Date: Wed, 6 Sep 2023 18:53:39 -0400 Subject: [PATCH 09/69] add a compile time option to allow disabling portable mode --- CMakeLists.txt | 2 ++ src/common/CMakeLists.txt | 4 ++++ src/common/fs/path_util.cpp | 6 ++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4039c680e8..9b39b295fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,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/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bf97d9ba28..8b420873a7 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 d71cfacc6a..dce219fcf8 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; From 4baaaf6a99ad763958bbc0ffa6d30ce75309f80a Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Thu, 7 Sep 2023 20:53:48 +0100 Subject: [PATCH 10/69] Do not reset the command buffer command count each time --- .../adsp/apps/audio_renderer/audio_renderer.cpp | 9 +++++++-- .../adsp/apps/audio_renderer/audio_renderer.h | 3 ++- .../audio_renderer/command_list_processor.cpp | 5 ----- .../apps/audio_renderer/command_list_processor.h | 8 -------- src/audio_core/renderer/system.cpp | 16 +++++----------- 5 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp index 3da342ea3e..2e549bc6f5 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp @@ -88,8 +88,13 @@ MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { return mailbox.Receive(dir, block); } -void AudioRenderer::SetCommandBuffer(s32 session_id, CommandBuffer& buffer) noexcept { - command_buffers[session_id] = buffer; +void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, + u64 applet_resource_user_id, bool reset) noexcept { + command_buffers[session_id].buffer = buffer; + command_buffers[session_id].size = size; + command_buffers[session_id].time_limit = time_limit; + command_buffers[session_id].applet_resource_user_id = applet_resource_user_id; + command_buffers[session_id].reset_buffer = reset; } u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h index b225e10fbf..3f5b7dca2a 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h @@ -75,7 +75,8 @@ public: void Send(Direction dir, MailboxMessage message); MailboxMessage Receive(Direction dir, bool block = true); - void SetCommandBuffer(s32 session_id, CommandBuffer& buffer) noexcept; + void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, + u64 applet_resource_user_id, bool reset) noexcept; u32 GetRemainCommandCount(s32 session_id) const noexcept; void ClearRemainCommandCount(s32 session_id) noexcept; u64 GetRenderingStartTick(s32 session_id) const noexcept; diff --git a/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp index acbc9100c7..24e4d04962 100644 --- a/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp @@ -37,11 +37,6 @@ u32 CommandListProcessor::GetRemainingCommandCount() const { return command_count - processed_command_count; } -void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) { - commands = reinterpret_cast(buffer + sizeof(Renderer::CommandListHeader)); - commands_buffer_size = size; -} - Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const { return stream; } diff --git a/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h index 9d6fe18511..4e5fb793e9 100644 --- a/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h @@ -56,14 +56,6 @@ public: */ u32 GetRemainingCommandCount() const; - /** - * Set the command buffer. - * - * @param buffer - The buffer to use. - * @param size - The size of the buffer. - */ - void SetBuffer(CpuAddr buffer, u64 size); - /** * Get the stream for this command list. * diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index 8f02754c52..d29754634e 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -609,17 +609,11 @@ void System::SendCommandToDsp() { time_limit_percent = 70.0f; } - AudioRenderer::CommandBuffer command_buffer{ - .buffer{translated_addr}, - .size{command_size}, - .time_limit{ - static_cast((time_limit_percent / 100) * 2'880'000.0 * - (static_cast(render_time_limit_percent) / 100.0f))}, - .applet_resource_user_id{applet_resource_user_id}, - .reset_buffer{reset_command_buffers}, - }; - - audio_renderer.SetCommandBuffer(session_id, command_buffer); + auto time_limit{ + static_cast((time_limit_percent / 100) * 2'880'000.0 * + (static_cast(render_time_limit_percent) / 100.0f))}; + audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit, + applet_resource_user_id, reset_command_buffers); reset_command_buffers = false; command_buffer_size = command_size; if (remaining_command_count == 0) { From 800d6f7d0d36701bacda9ec97c716515ed13fa6f Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Fri, 8 Sep 2023 07:17:52 +0100 Subject: [PATCH 11/69] Fix data source version 1 command looping --- .../renderer/command/data_source/adpcm.cpp | 6 + .../renderer/command/data_source/decode.cpp | 104 +++++++++--------- .../command/data_source/pcm_float.cpp | 6 + .../command/data_source/pcm_int16.cpp | 6 + 4 files changed, 71 insertions(+), 51 deletions(-) diff --git a/src/audio_core/renderer/command/data_source/adpcm.cpp b/src/audio_core/renderer/command/data_source/adpcm.cpp index 28e76fdcc1..e7f82d3b3e 100644 --- a/src/audio_core/renderer/command/data_source/adpcm.cpp +++ b/src/audio_core/renderer/command/data_source/adpcm.cpp @@ -20,6 +20,12 @@ void AdpcmDataSourceVersion1Command::Process(const AudioRenderer::CommandListPro auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count)}; + for (auto& wave_buffer : wave_buffers) { + wave_buffer.loop_start_offset = wave_buffer.start_offset; + wave_buffer.loop_end_offset = wave_buffer.end_offset; + wave_buffer.loop_count = wave_buffer.loop ? -1 : 0; + } + DecodeFromWaveBuffersArgs args{ .sample_format{SampleFormat::Adpcm}, .output{out_buffer}, diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp index 762aec8ad0..911dae3c1e 100644 --- a/src/audio_core/renderer/command/data_source/decode.cpp +++ b/src/audio_core/renderer/command/data_source/decode.cpp @@ -123,11 +123,13 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, return 0; } - auto samples_to_process{ - std::min(req.end_offset - req.start_offset - req.offset, req.samples_to_read)}; + auto start_pos{req.start_offset + req.offset}; + auto samples_to_process{std::min(req.end_offset - start_pos, req.samples_to_read)}; + if (samples_to_process == 0) { + return 0; + } auto samples_to_read{samples_to_process}; - auto start_pos{req.start_offset + req.offset}; auto samples_remaining_in_frame{start_pos % SamplesPerFrame}; auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame + samples_remaining_in_frame}; @@ -225,13 +227,24 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, * @param args - The wavebuffer data, and information for how to decode it. */ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) { + static constexpr auto EndWaveBuffer = [](auto& voice_state, auto& wavebuffer, auto& index, + auto& played_samples, auto& consumed) -> void { + voice_state.wave_buffer_valid[index] = false; + voice_state.loop_count = 0; + + if (wavebuffer.stream_ended) { + played_samples = 0; + } + + index = (index + 1) % MaxWaveBuffers; + consumed++; + }; auto& voice_state{*args.voice_state}; auto remaining_sample_count{args.sample_count}; auto fraction{voice_state.fraction}; - const auto sample_rate_ratio{ - (Common::FixedPoint<49, 15>(args.source_sample_rate) / args.target_sample_rate) * - args.pitch}; + const auto sample_rate_ratio{Common::FixedPoint<49, 15>( + (f32)args.source_sample_rate / (f32)args.target_sample_rate * (f32)args.pitch)}; const auto size_required{fraction + remaining_sample_count * sample_rate_ratio}; if (size_required < 0) { @@ -298,22 +311,23 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf auto end_offset{wavebuffer.end_offset}; if (wavebuffer.loop && voice_state.loop_count > 0 && - wavebuffer.loop_start_offset != 0 && wavebuffer.loop_end_offset != 0 && wavebuffer.loop_start_offset <= wavebuffer.loop_end_offset) { start_offset = wavebuffer.loop_start_offset; end_offset = wavebuffer.loop_end_offset; } - DecodeArg decode_arg{.buffer{wavebuffer.buffer}, - .buffer_size{wavebuffer.buffer_size}, - .start_offset{start_offset}, - .end_offset{end_offset}, - .channel_count{args.channel_count}, - .coefficients{}, - .adpcm_context{nullptr}, - .target_channel{args.channel}, - .offset{offset}, - .samples_to_read{samples_to_read - samples_read}}; + DecodeArg decode_arg{ + .buffer{wavebuffer.buffer}, + .buffer_size{wavebuffer.buffer_size}, + .start_offset{start_offset}, + .end_offset{end_offset}, + .channel_count{args.channel_count}, + .coefficients{}, + .adpcm_context{nullptr}, + .target_channel{args.channel}, + .offset{offset}, + .samples_to_read{samples_to_read - samples_read}, + }; s32 samples_decoded{0}; @@ -350,42 +364,30 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf temp_buffer_pos += samples_decoded; offset += samples_decoded; - if (samples_decoded == 0 || offset >= end_offset - start_offset) { - offset = 0; - if (!wavebuffer.loop) { - voice_state.wave_buffer_valid[wavebuffer_index] = false; - voice_state.loop_count = 0; + if (samples_decoded && offset < end_offset - start_offset) { + continue; + } - if (wavebuffer.stream_ended) { - played_sample_count = 0; - } - - wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers; - wavebuffers_consumed++; - } else { - voice_state.loop_count++; - if (wavebuffer.loop_count >= 0 && - (voice_state.loop_count > wavebuffer.loop_count || samples_decoded == 0)) { - voice_state.wave_buffer_valid[wavebuffer_index] = false; - voice_state.loop_count = 0; - - if (wavebuffer.stream_ended) { - played_sample_count = 0; - } - - wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers; - wavebuffers_consumed++; - } - - if (samples_decoded == 0) { - is_buffer_starved = true; - break; - } - - if (args.IsVoicePlayedSampleCountResetAtLoopPointSupported) { - played_sample_count = 0; - } + offset = 0; + if (wavebuffer.loop) { + voice_state.loop_count++; + if (wavebuffer.loop_count >= 0 && + (voice_state.loop_count > wavebuffer.loop_count || samples_decoded == 0)) { + EndWaveBuffer(voice_state, wavebuffer, wavebuffer_index, played_sample_count, + wavebuffers_consumed); } + + if (samples_decoded == 0) { + is_buffer_starved = true; + break; + } + + if (args.IsVoicePlayedSampleCountResetAtLoopPointSupported) { + played_sample_count = 0; + } + } else { + EndWaveBuffer(voice_state, wavebuffer, wavebuffer_index, played_sample_count, + wavebuffers_consumed); } } diff --git a/src/audio_core/renderer/command/data_source/pcm_float.cpp b/src/audio_core/renderer/command/data_source/pcm_float.cpp index 5cc0797f4d..d1f6856569 100644 --- a/src/audio_core/renderer/command/data_source/pcm_float.cpp +++ b/src/audio_core/renderer/command/data_source/pcm_float.cpp @@ -21,6 +21,12 @@ void PcmFloatDataSourceVersion1Command::Process( auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count); + for (auto& wave_buffer : wave_buffers) { + wave_buffer.loop_start_offset = wave_buffer.start_offset; + wave_buffer.loop_end_offset = wave_buffer.end_offset; + wave_buffer.loop_count = wave_buffer.loop ? -1 : 0; + } + DecodeFromWaveBuffersArgs args{ .sample_format{SampleFormat::PcmFloat}, .output{out_buffer}, diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.cpp b/src/audio_core/renderer/command/data_source/pcm_int16.cpp index 6499930680..c89a5aaac9 100644 --- a/src/audio_core/renderer/command/data_source/pcm_int16.cpp +++ b/src/audio_core/renderer/command/data_source/pcm_int16.cpp @@ -23,6 +23,12 @@ void PcmInt16DataSourceVersion1Command::Process( auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count); + for (auto& wave_buffer : wave_buffers) { + wave_buffer.loop_start_offset = wave_buffer.start_offset; + wave_buffer.loop_end_offset = wave_buffer.end_offset; + wave_buffer.loop_count = wave_buffer.loop ? -1 : 0; + } + DecodeFromWaveBuffersArgs args{ .sample_format{SampleFormat::PcmInt16}, .output{out_buffer}, From 7bec8d1c5bd6df49fea138d907bdd9e5b042ff51 Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 8 Sep 2023 14:00:07 -0400 Subject: [PATCH 12/69] internal_network: log error on interrupt pipe read failure --- src/core/internal_network/network.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index ef5e5d0135..a983f23ea4 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -201,7 +201,10 @@ void InterruptSocketOperations() { void AcknowledgeInterrupt() { u8 value = 0; - read(interrupt_pipe_fd[0], &value, sizeof(value)); + 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() { From 254b2bd9dfb71e7754b6f923735afbf97b01f2da Mon Sep 17 00:00:00 2001 From: GPUCode Date: Fri, 8 Sep 2023 22:43:54 +0300 Subject: [PATCH 13/69] cmake: Add option to fetch validation layer binary on android --- CMakeLists.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4039c680e8..c6b1589853 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,8 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}") option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) +option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON) + CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF) CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF) @@ -77,6 +79,24 @@ if (ANDROID OR WIN32 OR APPLE) endif() option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL}) +if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL) + set(vvl_version "sdk-1.3.261.1") + set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip") + if (NOT EXISTS "${vvl_zip_file}") + # Download and extract validation layer release to externals directory + set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download") + file(DOWNLOAD "${vvl_base_url}/${vvl_version}/android-binaries-${vvl_version}-android.zip" + "${vvl_zip_file}" SHOW_PROGRESS) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals") + endif() + + # Copy the arm64 binary to src/android/app/main/jniLibs + set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/") + file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so" + DESTINATION "${vvl_lib_path}") +endif() + # On Android, fetch and compile libcxx before doing anything else if (ANDROID) set(CMAKE_SKIP_INSTALL_RULES ON) From cad28abe613638cddf25569f746a602262352dab Mon Sep 17 00:00:00 2001 From: GPUCode Date: Fri, 8 Sep 2023 22:47:39 +0300 Subject: [PATCH 14/69] renderer_vulkan: Remove debug report * VVL has implemented the more modern alternative, thus we don't need to support it anymore --- .../renderer_vulkan/renderer_vulkan.cpp | 18 ++----------- .../renderer_vulkan/renderer_vulkan.h | 4 +-- .../vulkan_common/vulkan_debug_callback.cpp | 27 ------------------- .../vulkan_common/vulkan_debug_callback.h | 2 -- .../vulkan_common/vulkan_instance.cpp | 8 +++--- 5 files changed, 6 insertions(+), 53 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 454bb66a47..c4c30d8071 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -66,21 +66,6 @@ std::string BuildCommaSeparatedExtensions( return fmt::format("{}", fmt::join(available_extensions, ",")); } -DebugCallback MakeDebugCallback(const vk::Instance& instance, const vk::InstanceDispatch& dld) { - if (!Settings::values.renderer_debug) { - return DebugCallback{}; - } - const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld); - const auto it = std::ranges::find_if(*properties, [](const auto& prop) { - return std::strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, prop.extensionName) == 0; - }); - if (it != properties->end()) { - return CreateDebugUtilsCallback(instance); - } else { - return CreateDebugReportCallback(instance); - } -} - } // Anonymous namespace Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, @@ -103,7 +88,8 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())), instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, Settings::values.renderer_debug.GetValue())), - debug_callback(MakeDebugCallback(instance, dld)), + debug_messenger(Settings::values.renderer_debug ? CreateDebugUtilsCallback(instance) + : vk::DebugUtilsMessenger{}), surface(CreateSurface(instance, render_window.GetWindowInfo())), device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(), scheduler(device, state_tracker), diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 89e98425ef..590bc1c644 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -35,8 +35,6 @@ class GPU; namespace Vulkan { -using DebugCallback = std::variant; - Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, VkSurfaceKHR surface); @@ -75,7 +73,7 @@ private: vk::InstanceDispatch dld; vk::Instance instance; - DebugCallback debug_callback; + vk::DebugUtilsMessenger debug_messenger; vk::SurfaceKHR surface; ScreenInfo screen_info; diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index 67e8065a47..448df2d3ab 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp @@ -63,22 +63,6 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, return VK_FALSE; } -VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, - uint64_t object, size_t location, int32_t messageCode, - const char* pLayerPrefix, const char* pMessage, void* pUserData) { - const VkDebugReportFlagBitsEXT severity = static_cast(flags); - const std::string_view message{pMessage}; - if (severity & VK_DEBUG_REPORT_ERROR_BIT_EXT) { - LOG_CRITICAL(Render_Vulkan, "{}", message); - } else if (severity & VK_DEBUG_REPORT_WARNING_BIT_EXT) { - LOG_WARNING(Render_Vulkan, "{}", message); - } else if (severity & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) { - LOG_INFO(Render_Vulkan, "{}", message); - } else if (severity & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { - LOG_DEBUG(Render_Vulkan, "{}", message); - } - return VK_FALSE; -} } // Anonymous namespace vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) { @@ -98,15 +82,4 @@ vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) { }); } -vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance) { - return instance.CreateDebugReportCallback({ - .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, - .pNext = nullptr, - .flags = VK_DEBUG_REPORT_DEBUG_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT | - VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, - .pfnCallback = DebugReportCallback, - .pUserData = nullptr, - }); -} - } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h index a8af7b406b..5e940782f8 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.h +++ b/src/video_core/vulkan_common/vulkan_debug_callback.h @@ -9,6 +9,4 @@ namespace Vulkan { vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance); -vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance); - } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index bc16145be5..180657a75a 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -76,11 +76,9 @@ namespace { extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); } #endif - if (enable_validation) { - const bool debug_utils = - AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}); - extensions.push_back(debug_utils ? VK_EXT_DEBUG_UTILS_EXTENSION_NAME - : VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + if (enable_validation && + AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME})) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } return extensions; } From 24ab10c2f601afb9c241f6320c5c8bac6a3d3b22 Mon Sep 17 00:00:00 2001 From: GPUCode <47210458+GPUCode@users.noreply.github.com> Date: Sun, 10 Sep 2023 03:19:45 +0300 Subject: [PATCH 15/69] vk_buffer_cache: Respect max vertex bindings in BindVertexBuffers (#11471) --- src/video_core/renderer_vulkan/vk_buffer_cache.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 60a6ac6517..e15865d16a 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -529,17 +529,20 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bi buffer_handles.push_back(handle); } if (device.IsExtExtendedDynamicStateSupported()) { - scheduler.Record([bindings_ = std::move(bindings), + scheduler.Record([this, bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, - bindings_.max_index - bindings_.min_index, + std::min(bindings_.max_index - bindings_.min_index, + device.GetMaxVertexInputBindings()), buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data(), bindings_.strides.data()); }); } else { - scheduler.Record([bindings_ = std::move(bindings), + scheduler.Record([this, bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffers(bindings_.min_index, bindings_.max_index - bindings_.min_index, + cmdbuf.BindVertexBuffers(bindings_.min_index, + std::min(bindings_.max_index - bindings_.min_index, + device.GetMaxVertexInputBindings()), buffer_handles_.data(), bindings_.offsets.data()); }); } From 36917d8a8f364857f2a54d22b8dfa39c54534f13 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Thu, 7 Sep 2023 00:15:15 +0200 Subject: [PATCH 16/69] am: Remove bcat from PopLaunchParameter This never belonged here and has no use anymore since the Boxcat backend was removed. . --- src/core/hle/service/am/am.cpp | 23 ++++------------------- src/core/hle/service/am/am.h | 1 - 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index e92f400de2..42e00c30ac 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,25 +1518,10 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto kind = rp.PopEnum(); - LOG_DEBUG(Service_AM, "called, kind={:08X}", kind); + LOG_WARNING(Service_AM, "(STUBBED) 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(system, std::move(*data)); - launch_popped_application_specific = true; - return; - } + if (kind == LaunchParameterKind::UserChannel) { + LOG_ERROR(Service_AM, "Popping from UserChannel is not supported!"); } else if (kind == LaunchParameterKind::AccountPreselectedUser && !launch_popped_account_preselect) { LaunchParameterAccountPreselectedUser params{}; diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index d68998f049..f75a665b25 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -339,7 +339,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; From 87c0ba129ce38dd3b001fbef8021590a127fb1a8 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Sun, 10 Sep 2023 02:36:26 +0200 Subject: [PATCH 17/69] am: Implement UserChannel parameters Used by the Super Mairo 3D All-Stars collection. --- src/android/app/src/main/jni/native.cpp | 1 + src/core/core.cpp | 6 ++++ src/core/core.h | 7 +++++ src/core/hle/service/am/am.cpp | 40 ++++++++++++++++++------- src/yuzu/game_list.cpp | 6 ++-- src/yuzu/game_list.h | 3 +- src/yuzu/main.cpp | 14 ++++++--- src/yuzu/main.h | 11 +++++-- src/yuzu_cmd/yuzu.cpp | 1 + 9 files changed, 70 insertions(+), 19 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 0f2a6d9e4e..b9ecefa745 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -270,6 +270,7 @@ public: m_vulkan_library); m_system.SetFilesystem(m_vfs); + m_system.GetUserChannel().clear(); // Initialize system. jauto android_keyboard = std::make_unique(); diff --git a/src/core/core.cpp b/src/core/core.cpp index e95ae80da1..f075ae7fa2 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -562,6 +562,8 @@ struct System::Impl { std::array gpu_dirty_memory_write_manager{}; + + std::deque> user_channel; }; System::System() : impl{std::make_unique(*this)} {} @@ -1036,6 +1038,10 @@ void System::ExecuteProgram(std::size_t program_index) { } } +std::deque>& 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 a9ff9315e3..fba3121257 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -459,6 +460,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>& GetUserChannel(); + /// Type used for the frontend to designate a callback for System to exit the application. using ExitCallback = std::function; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 42e00c30ac..f9c4f9678c 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1518,12 +1518,26 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto kind = rp.PopEnum(); - LOG_WARNING(Service_AM, "(STUBBED) called, kind={:08X}", kind); + LOG_INFO(Service_AM, "called, kind={:08X}", kind); if (kind == LaunchParameterKind::UserChannel) { - LOG_ERROR(Service_AM, "Popping from UserChannel is not supported!"); + 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(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; @@ -1535,7 +1549,6 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { params.current_user = *uuid; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); std::vector buffer(sizeof(LaunchParameterAccountPreselectedUser)); @@ -1543,12 +1556,11 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { rb.PushIpcInterface(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) { @@ -1840,14 +1852,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().lock(); + if (storage) { + system.GetUserChannel().push_back(storage->GetData()); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b5a02700d5..c29d762c2b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -588,10 +588,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 6c2f75e53e..45d6dee88c 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/main.cpp b/src/yuzu/main.cpp index 4e435c7e2a..538174462f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1698,7 +1698,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(); @@ -1710,6 +1711,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(*this), // Amiibo Settings (UISettings::values.controller_applet_disabled.GetValue() == true) @@ -1849,7 +1854,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 @@ -1893,7 +1898,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; } @@ -3314,7 +3319,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() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 668dbc3b13..fd8b196c39 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(); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index c1695cc6e4..55d0938f7e 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()); system.SetFilesystem(std::make_shared()); system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); + system.GetUserChannel().clear(); const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)}; From baad1238c3e111551e4012c56772d0068397ccd0 Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Mon, 11 Sep 2023 02:54:32 +0100 Subject: [PATCH 18/69] Look for the most recently modified image for present --- src/video_core/texture_cache/texture_cache.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 4457b366f1..1bdb0def5f 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

::TryFindFramebufferImageView(VAddr cpu_ad return nullptr; } const auto& image_map_ids = it->second; + boost::container::small_vector 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

::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; } From 0cdc8b13b743e4d7256d9337c7f01296cb98b7bd Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 10 Sep 2023 20:43:26 -0600 Subject: [PATCH 19/69] service: mii: Add mii util and result --- src/core/CMakeLists.txt | 2 + src/core/hle/service/mii/mii.cpp | 11 ++--- src/core/hle/service/mii/mii_manager.cpp | 9 ++-- src/core/hle/service/mii/mii_result.h | 20 ++++++++ src/core/hle/service/mii/mii_util.h | 58 ++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 src/core/hle/service/mii/mii_result.h create mode 100644 src/core/hle/service/mii/mii_util.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 012648d694..e9095ac529 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -588,6 +588,8 @@ add_library(core STATIC hle/service/mii/mii.h hle/service/mii/mii_manager.cpp hle/service/mii/mii_manager.h + hle/service/mii/mii_result.h + hle/service/mii/mii_util.h hle/service/mii/raw_data.cpp hle/service/mii/raw_data.h hle/service/mii/types.h diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 65c11a2f3e..bf3ee59073 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -7,13 +7,12 @@ #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 { public: explicit IDatabaseService(Core::System& system_) @@ -162,21 +161,21 @@ private: if (age > Age::All) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); + rb.Push(ResultInvalidArgument); LOG_ERROR(Service_Mii, "invalid age={}", age); return; } if (gender > Gender::All) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); + rb.Push(ResultInvalidArgument); LOG_ERROR(Service_Mii, "invalid gender={}", gender); return; } if (race > Race::All) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); + rb.Push(ResultInvalidArgument); LOG_ERROR(Service_Mii, "invalid race={}", race); return; } @@ -196,7 +195,7 @@ private: 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; } diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index dd632df506..392aa78daf 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -10,14 +10,13 @@ #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/mii/mii_result.h" #include "core/hle/service/mii/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'}; @@ -410,11 +409,11 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const { Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& 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) { @@ -695,7 +694,7 @@ Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) { 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; } } // namespace Service::Mii 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 0000000000..021cb76da2 --- /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_util.h b/src/core/hle/service/mii/mii_util.h new file mode 100644 index 0000000000..5eb6df317f --- /dev/null +++ b/src/core/hle/service/mii/mii_util.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#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(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(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 + static T GetRandomValue(T min, T max) { + std::random_device device; + std::mt19937 gen(device()); + std::uniform_int_distribution distribution(static_cast(min), + static_cast(max)); + return static_cast(distribution(gen)); + } + + template + static T GetRandomValue(T max) { + return GetRandomValue({}, max); + } + + static bool IsFontRegionValid(FontRegion font, std::span text) { + // Todo:: This function needs to check against the font tables + return true; + } +}; +} // namespace Service::Mii From 63b239f5c6610bb1d3a31affce413951dd805e58 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 10 Sep 2023 21:19:31 -0600 Subject: [PATCH 20/69] service: mii: Move all raw data to it's file --- src/core/hle/service/mii/mii_manager.cpp | 179 +++----- src/core/hle/service/mii/mii_util.h | 2 +- src/core/hle/service/mii/raw_data.cpp | 545 ++++++++++++++--------- src/core/hle/service/mii/raw_data.h | 70 ++- src/core/hle/service/mii/types.h | 62 +-- 5 files changed, 478 insertions(+), 380 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 392aa78daf..de70c3da6f 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -11,6 +11,7 @@ #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/mii_result.h" +#include "core/hle/service/mii/mii_util.h" #include "core/hle/service/mii/raw_data.h" namespace Service::Mii { @@ -19,19 +20,7 @@ namespace { 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 HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7}; -constexpr std::array EyeColorLookup{8, 9, 10, 11, 12, 13}; -constexpr std::array MouthColorLookup{19, 20, 21, 22, 23}; -constexpr std::array GlassesColorLookup{8, 14, 15, 16, 17, 18, 0}; -constexpr std::array 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 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}}; +constexpr Nickname DefaultMiiName{u'n', u'o', u' ', u'n', u'a', u'm', u'e'}; template std::array ResizeArray(const std::array& in) { @@ -100,42 +89,15 @@ CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) { }; } -u16 GenerateCrc16(const void* data, std::size_t size) { - s32 crc{}; - for (std::size_t i = 0; i < size; i++) { - crc ^= static_cast(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(crc)); -} - -template -T GetRandomValue(T min, T max) { - std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution distribution(static_cast(min), static_cast(max)); - return static_cast(distribution(gen)); -} - -template -T GetRandomValue(T max) { - return GetRandomValue({}, max); -} - MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { MiiStoreBitFields bf{}; if (gender == Gender::All) { - gender = GetRandomValue(Gender::Maximum); + gender = MiiUtil::GetRandomValue(Gender::Maximum); } bf.gender.Assign(gender); - bf.favorite_color.Assign(GetRandomValue(11)); + bf.favorite_color.Assign(MiiUtil::GetRandomValue(11)); bf.region_move.Assign(0); bf.font_region.Assign(FontRegion::Standard); bf.type.Assign(0); @@ -143,7 +105,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo bf.build.Assign(64); if (age == Age::All) { - const auto temp{GetRandomValue(10)}; + const auto temp{MiiUtil::GetRandomValue(10)}; if (temp >= 8) { age = Age::Old; } else if (temp >= 4) { @@ -154,7 +116,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo } if (race == Race::All) { - const auto temp{GetRandomValue(10)}; + const auto temp{MiiUtil::GetRandomValue(10)}; if (temp >= 8) { race = Race::Black; } else if (temp >= 4) { @@ -166,56 +128,57 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo u32 axis_y{}; if (gender == Gender::Female && age == Age::Young) { - axis_y = GetRandomValue(3); + axis_y = MiiUtil::GetRandomValue(3); } const std::size_t index{3 * static_cast(age) + 9 * static_cast(gender) + static_cast(race)}; - const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)}; - const auto faceline_color_info{RawData::RandomMiiFacelineColor.at( + const auto& faceline_type_info{RawData::RandomMiiFaceline.at(index)}; + const auto& faceline_color_info{RawData::RandomMiiFacelineColor.at( 3 * static_cast(gender) + static_cast(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(race) + - static_cast(age))}; - const auto eye_type_info{RawData::RandomMiiEyeType.at(index)}; - const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast(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(age))}; + const auto& faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)}; + const auto& faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)}; + const auto& hair_type_info{RawData::RandomMiiHairType.at(index)}; + const auto& hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast(race) + + static_cast(age))}; + const auto& eye_type_info{RawData::RandomMiiEyeType.at(index)}; + const auto& eye_color_info{RawData::RandomMiiEyeColor.at(static_cast(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(age))}; bf.faceline_type.Assign( - faceline_type_info.values[GetRandomValue(faceline_type_info.values_count)]); + faceline_type_info + .values[MiiUtil::GetRandomValue(faceline_type_info.values_count)]); bf.faceline_color.Assign( - faceline_color_info.values[GetRandomValue(faceline_color_info.values_count)]); + faceline_color_info + .values[MiiUtil::GetRandomValue(faceline_color_info.values_count)]); bf.faceline_wrinkle.Assign( faceline_wrinkle_info - .values[GetRandomValue(faceline_wrinkle_info.values_count)]); + .values[MiiUtil::GetRandomValue(faceline_wrinkle_info.values_count)]); bf.faceline_makeup.Assign( faceline_makeup_info - .values[GetRandomValue(faceline_makeup_info.values_count)]); + .values[MiiUtil::GetRandomValue(faceline_makeup_info.values_count)]); bf.hair_type.Assign( - hair_type_info.values[GetRandomValue(hair_type_info.values_count)]); - bf.hair_color.Assign( - HairColorLookup[hair_color_info - .values[GetRandomValue(hair_color_info.values_count)]]); - bf.hair_flip.Assign(GetRandomValue(HairFlip::Maximum)); + hair_type_info.values[MiiUtil::GetRandomValue(hair_type_info.values_count)]); + bf.hair_color.Assign(RawData::GetHairColorFromVer3( + hair_color_info + .values[MiiUtil::GetRandomValue(hair_color_info.values_count)])); + bf.hair_flip.Assign(MiiUtil::GetRandomValue(HairFlip::Maximum)); bf.eye_type.Assign( - eye_type_info.values[GetRandomValue(eye_type_info.values_count)]); + eye_type_info.values[MiiUtil::GetRandomValue(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]}; + const auto eye_rotate_offset{32 - RawData::EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; + const auto eye_rotate{32 - RawData::EyeRotateLookup[bf.eye_type]}; - bf.eye_color.Assign( - EyeColorLookup[eye_color_info - .values[GetRandomValue(eye_color_info.values_count)]]); + bf.eye_color.Assign(RawData::GetEyeColorFromVer3( + eye_color_info.values[MiiUtil::GetRandomValue(eye_color_info.values_count)])); bf.eye_scale.Assign(4); bf.eye_aspect.Assign(3); bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate); @@ -223,13 +186,14 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo bf.eye_y.Assign(axis_y + 12); bf.eyebrow_type.Assign( - eyebrow_type_info.values[GetRandomValue(eyebrow_type_info.values_count)]); + eyebrow_type_info + .values[MiiUtil::GetRandomValue(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_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6}; const auto eyebrow_rotate{ - 32 - EyebrowRotateLookup[static_cast(bf.eyebrow_type.Value())]}; + 32 - RawData::EyebrowRotateLookup[static_cast(bf.eyebrow_type.Value())]}; bf.eyebrow_color.Assign(bf.hair_color); bf.eyebrow_scale.Assign(4); @@ -241,15 +205,15 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo const auto nose_scale{gender == Gender::Female ? 3 : 4}; bf.nose_type.Assign( - nose_type_info.values[GetRandomValue(nose_type_info.values_count)]); + nose_type_info.values[MiiUtil::GetRandomValue(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(4) : 0}; + const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue(4) : 0}; bf.mouth_type.Assign( - mouth_type_info.values[GetRandomValue(mouth_type_info.values_count)]); - bf.mouth_color.Assign(MouthColorLookup[mouth_color]); + mouth_type_info.values[MiiUtil::GetRandomValue(mouth_type_info.values_count)]); + bf.mouth_color.Assign(RawData::GetMouthColorFromVer3(mouth_color)); bf.mouth_scale.Assign(4); bf.mouth_aspect.Assign(3); bf.mouth_y.Assign(axis_y + 13); @@ -257,22 +221,22 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo bf.beard_color.Assign(bf.hair_color); bf.mustache_scale.Assign(4); - if (gender == Gender::Male && age != Age::Young && GetRandomValue(10) < 2) { + if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue(10) < 2) { const auto mustache_and_beard_flag{ - GetRandomValue(BeardAndMustacheFlag::All)}; + 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 = GetRandomValue(BeardType::Beard1, BeardType::Beard5); + beard_type = MiiUtil::GetRandomValue(BeardType::Beard1, BeardType::Beard5); } if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == BeardAndMustacheFlag::Mustache) { - mustache_type = - GetRandomValue(MustacheType::Mustache1, MustacheType::Mustache5); + mustache_type = MiiUtil::GetRandomValue(MustacheType::Mustache1, + MustacheType::Mustache5); } bf.mustache_type.Assign(mustache_type); @@ -284,7 +248,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo bf.mustache_y.Assign(axis_y + 10); } - const auto glasses_type_start{GetRandomValue(100)}; + const auto glasses_type_start{MiiUtil::GetRandomValue(100)}; u8 glasses_type{}; while (glasses_type_start < glasses_type_info.values[glasses_type]) { if (++glasses_type >= glasses_type_info.values_count) { @@ -294,7 +258,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo } bf.glasses_type.Assign(glasses_type); - bf.glasses_color.Assign(GlassesColorLookup[0]); + bf.glasses_color.Assign(RawData::GetGlassColorFromVer3(0)); bf.glasses_scale.Assign(4); bf.glasses_y.Assign(axis_y + 10); @@ -315,23 +279,23 @@ MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& u bf.height.Assign(info.height); bf.build.Assign(info.weight); bf.type.Assign(info.type); - bf.region_move.Assign(info.region); + bf.region_move.Assign(info.region_move); 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_color.Assign(RawData::GetHairColorFromVer3(info.hair_color)); bf.hair_flip.Assign(static_cast(info.hair_flip)); bf.eye_type.Assign(info.eye_type); - bf.eye_color.Assign(EyeColorLookup[info.eye_color]); + bf.eye_color.Assign(RawData::GetEyeColorFromVer3(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_color.Assign(RawData::GetHairColorFromVer3(info.eyebrow_color)); bf.eyebrow_scale.Assign(info.eyebrow_scale); bf.eyebrow_aspect.Assign(info.eyebrow_aspect); bf.eyebrow_rotate.Assign(info.eyebrow_rotate); @@ -341,17 +305,17 @@ MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& u 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_color.Assign(RawData::GetMouthColorFromVer3(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_color.Assign(RawData::GetHairColorFromVer3(info.beard_color)); bf.beard_type.Assign(static_cast(info.beard_type)); bf.mustache_type.Assign(static_cast(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_color.Assign(RawData::GetGlassColorFromVer3(static_cast(info.glasses_color))); bf.glasses_scale.Assign(info.glasses_scale); bf.glasses_y.Assign(info.glasses_y); bf.mole_type.Assign(info.mole_type); @@ -366,14 +330,14 @@ MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& u MiiStoreData::MiiStoreData() = default; -MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, +MiiStoreData::MiiStoreData(const Nickname& 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)); + data_crc = MiiUtil::CalculateCrc16(data.data.data(), sizeof(data)); + device_crc = MiiUtil::CalculateCrc16(&user_id, sizeof(Common::UUID)); } MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {} @@ -580,16 +544,19 @@ Ver3StoreData MiiManager::BuildFromStoreData(const CharInfo& mii) const { mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); // 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]); + mii_v3.appearance_bits1.skin_color.Assign( + RawData::FromVer3GetFacelineColor(mii.faceline_color)); + mii_v3.appearance_bits3.hair_color.Assign(RawData::FromVer3GetHairColor(mii.hair_color)); + mii_v3.appearance_bits4.eye_color.Assign(RawData::FromVer3GetEyeColor(mii.eye_color)); + mii_v3.appearance_bits5.eyebrow_color.Assign(RawData::FromVer3GetHairColor(mii.eyebrow_color)); + mii_v3.appearance_bits7.mouth_color.Assign(RawData::FromVer3GetMouthlineColor(mii.mouth_color)); + mii_v3.appearance_bits9.facial_hair_color.Assign( + RawData::FromVer3GetHairColor(mii.beard_color)); + mii_v3.appearance_bits10.glasses_color.Assign( + RawData::FromVer3GetGlassColor(mii.glasses_color)); + mii_v3.appearance_bits10.glasses_type.Assign(RawData::FromVer3GetGlassType(mii.glasses_type)); - mii_v3.crc = GenerateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16)); + mii_v3.crc = MiiUtil::CalculateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16)); // TODO: Validate mii_v3 data diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h index 5eb6df317f..d98f838250 100644 --- a/src/core/hle/service/mii/mii_util.h +++ b/src/core/hle/service/mii/mii_util.h @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "common/uuid.h" -#include "core/hle/service/mii/mii_types.h" +#include "core/hle/service/mii/types.h" namespace Service::Mii { class MiiUtil { diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/raw_data.cpp index e5245b7917..070e2d199d 100644 --- a/src/core/hle/service/mii/raw_data.cpp +++ b/src/core/hle/service/mii/raw_data.cpp @@ -5,6 +5,83 @@ namespace Service::Mii::RawData { +constexpr std::array FromVer3FacelineColorTable{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, +}; + +constexpr std::array 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 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 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 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 FromVer3GlassTypeTable{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, + 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7, +}; + +constexpr std::array Ver3FacelineColorTable{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, +}; + +constexpr std::array Ver3HairColorTable{ + 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, +}; + +constexpr std::array Ver3EyeColorTable{ + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, +}; + +constexpr std::array Ver3MouthColorTable{ + 0x13, 0x14, 0x15, 0x16, 0x17, +}; + +constexpr std::array Ver3GlassColorTable{ + 0x8, 0xe, 0xf, 0x10, 0x11, 0x12, 0x0, +}; + +const std::array 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 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 BaseMii{ Service::Mii::DefaultMii{ .face_type = 0, @@ -13,7 +90,7 @@ const std::array BaseMii{ .face_makeup = 0, .hair_type = 33, .hair_color = 1, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 2, .eye_color = 0, .eye_scale = 4, @@ -36,8 +113,8 @@ const std::array BaseMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -53,9 +130,10 @@ const std::array BaseMii{ .weight = 64, .gender = Gender::Male, .favorite_color = 0, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -64,7 +142,7 @@ const std::array BaseMii{ .face_makeup = 0, .hair_type = 12, .hair_color = 1, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 4, .eye_color = 0, .eye_scale = 4, @@ -87,8 +165,8 @@ const std::array BaseMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -104,9 +182,10 @@ const std::array BaseMii{ .weight = 64, .gender = Gender::Female, .favorite_color = 0, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, }; @@ -118,7 +197,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 68, .hair_color = 0, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 2, .eye_color = 0, .eye_scale = 4, @@ -141,8 +220,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -158,9 +237,10 @@ const std::array DefaultMii{ .weight = 64, .gender = Gender::Male, .favorite_color = 4, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -169,7 +249,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 55, .hair_color = 6, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 2, .eye_color = 4, .eye_scale = 4, @@ -192,8 +272,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -209,9 +289,10 @@ const std::array DefaultMii{ .weight = 64, .gender = Gender::Male, .favorite_color = 5, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -220,7 +301,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 33, .hair_color = 1, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 2, .eye_color = 0, .eye_scale = 4, @@ -243,8 +324,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -260,9 +341,10 @@ const std::array DefaultMii{ .weight = 64, .gender = Gender::Male, .favorite_color = 0, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -271,7 +353,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 24, .hair_color = 0, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 4, .eye_color = 0, .eye_scale = 4, @@ -294,8 +376,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -311,9 +393,10 @@ const std::array DefaultMii{ .weight = 64, .gender = Gender::Female, .favorite_color = 2, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -322,7 +405,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 14, .hair_color = 7, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 4, .eye_color = 5, .eye_scale = 4, @@ -345,8 +428,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -362,9 +445,10 @@ const std::array DefaultMii{ .weight = 64, .gender = Gender::Female, .favorite_color = 6, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -373,7 +457,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 12, .hair_color = 1, - .hair_flip = 0, + .hair_flip = HairFlip::Left, .eye_type = 4, .eye_color = 0, .eye_scale = 4, @@ -396,8 +480,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = 0, - .beard_type = 0, + .mustache_type = MustacheType::None, + .beard_type = BeardType::None, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -413,134 +497,135 @@ const std::array DefaultMii{ .weight = 64, .gender = Gender::Female, .favorite_color = 7, - .region = 0, + .region_move = 0, .font_region = FontRegion::Standard, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, }; -const std::array RandomMiiFaceline{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiFaceline{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, .values_count = 13, .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, .values_count = 13, .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -549,38 +634,38 @@ const std::array RandomMiiFaceline{ }, }; -const std::array RandomMiiFacelineColor{ - Service::Mii::RandomMiiData3{ +const std::array 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,127 +673,127 @@ const std::array RandomMiiFacelineColor{ }, }; -const std::array RandomMiiFacelineWrinkle{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiFacelineWrinkle{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, .values_count = 20, .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, .values_count = 20, .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, .values_count = 20, .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, .values_count = 20, .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, .values_count = 20, .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -717,127 +802,127 @@ const std::array RandomMiiFacelineWrinkle{ }, }; -const std::array RandomMiiFacelineMakeup{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiFacelineMakeup{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, .values_count = 20, .values = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -846,8 +931,8 @@ const std::array RandomMiiFacelineMakeup{ }, }; -const std::array RandomMiiHairType{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiHairType{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, @@ -855,7 +940,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, @@ -863,7 +948,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, @@ -871,7 +956,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, @@ -879,7 +964,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, @@ -887,7 +972,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, @@ -895,28 +980,28 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, .values_count = 18, .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, .values_count = 19, .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, .values_count = 19, .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, @@ -924,7 +1009,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, @@ -933,7 +1018,7 @@ const std::array RandomMiiHairType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, @@ -942,7 +1027,7 @@ const std::array RandomMiiHairType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, @@ -951,7 +1036,7 @@ const std::array RandomMiiHairType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, @@ -960,7 +1045,7 @@ const std::array RandomMiiHairType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, @@ -969,7 +1054,7 @@ const std::array RandomMiiHairType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, @@ -977,7 +1062,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, @@ -985,7 +1070,7 @@ const std::array RandomMiiHairType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -996,55 +1081,55 @@ const std::array RandomMiiHairType{ }; const std::array 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,8 +1137,8 @@ const std::array RandomMiiHairColor{ }, }; -const std::array RandomMiiEyeType{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiEyeType{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, @@ -1061,7 +1146,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, @@ -1069,7 +1154,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, @@ -1077,7 +1162,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, @@ -1085,7 +1170,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, @@ -1093,7 +1178,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, @@ -1101,7 +1186,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, @@ -1109,7 +1194,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, @@ -1117,7 +1202,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, @@ -1125,7 +1210,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, @@ -1133,7 +1218,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, @@ -1141,7 +1226,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, @@ -1149,7 +1234,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, @@ -1158,7 +1243,7 @@ const std::array RandomMiiEyeType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, @@ -1167,7 +1252,7 @@ const std::array RandomMiiEyeType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, @@ -1176,7 +1261,7 @@ const std::array RandomMiiEyeType{ 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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, @@ -1184,7 +1269,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, @@ -1192,7 +1277,7 @@ const std::array RandomMiiEyeType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -1202,47 +1287,47 @@ const std::array RandomMiiEyeType{ }, }; -const std::array RandomMiiEyeColor{ - Service::Mii::RandomMiiData2{ +const std::array 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 RandomMiiEyebrowType{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiEyebrowType{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, @@ -1250,7 +1335,7 @@ const std::array RandomMiiEyebrowType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, @@ -1258,7 +1343,7 @@ const std::array RandomMiiEyebrowType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, @@ -1266,84 +1351,84 @@ const std::array RandomMiiEyebrowType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, .values_count = 21, .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, .values_count = 21, .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, .values_count = 21, .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -1352,127 +1437,127 @@ const std::array RandomMiiEyebrowType{ }, }; -const std::array RandomMiiNoseType{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiNoseType{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, .values_count = 15, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, .values_count = 15, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, .values_count = 12, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, .values_count = 11, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, .values_count = 10, .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, .values_count = 12, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, .values_count = 11, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -1481,8 +1566,8 @@ const std::array RandomMiiNoseType{ }, }; -const std::array RandomMiiMouthType{ - Service::Mii::RandomMiiData4{ +const std::array RandomMiiMouthType{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Black, @@ -1490,7 +1575,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Black, @@ -1498,7 +1583,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Black, @@ -1506,7 +1591,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::White, @@ -1514,7 +1599,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::White, @@ -1522,7 +1607,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::White, @@ -1530,7 +1615,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Young, .race = Race::Asian, @@ -1538,7 +1623,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Normal, .race = Race::Asian, @@ -1546,7 +1631,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Male, .age = Age::Old, .race = Race::Asian, @@ -1554,7 +1639,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Black, @@ -1562,7 +1647,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Black, @@ -1570,7 +1655,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Black, @@ -1578,7 +1663,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::White, @@ -1586,7 +1671,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::White, @@ -1594,7 +1679,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::White, @@ -1602,7 +1687,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Young, .race = Race::Asian, @@ -1610,7 +1695,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Normal, .race = Race::Asian, @@ -1618,7 +1703,7 @@ const std::array RandomMiiMouthType{ .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{ + RandomMiiData4{ .gender = Gender::Female, .age = Age::Old, .race = Race::Asian, @@ -1628,22 +1713,66 @@ const std::array RandomMiiMouthType{ }, }; -const std::array RandomMiiGlassType{ - Service::Mii::RandomMiiData2{ +const std::array 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]; +} + +u8 GetFacelineColorFromVer3(u8 color) { + return Ver3FacelineColorTable[color]; +} + +u8 GetHairColorFromVer3(u32 color) { + return Ver3HairColorTable[color]; +} + +u8 GetEyeColorFromVer3(u32 color) { + return Ver3EyeColorTable[color]; +} + +u8 GetMouthColorFromVer3(u32 color) { + return Ver3MouthColorTable[color]; +} + +u8 GetGlassColorFromVer3(u8 color) { + return Ver3GlassColorTable[color]; +} + } // namespace Service::Mii::RawData diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h index cdd2337d6a..ab84d09a1f 100644 --- a/src/core/hle/service/mii/raw_data.h +++ b/src/core/hle/service/mii/raw_data.h @@ -9,19 +9,65 @@ namespace Service::Mii::RawData { +struct RandomMiiValues { + std::array values{}; +}; +static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); + +struct RandomMiiData4 { + Gender gender{}; + Age age{}; + Race race{}; + u32 values_count{}; + std::array values{}; +}; +static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); + +struct RandomMiiData3 { + u32 arg_1; + u32 arg_2; + u32 values_count; + std::array values{}; +}; +static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); + +struct RandomMiiData2 { + u32 arg_1; + u32 values_count; + std::array values{}; +}; +static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); + extern const std::array BaseMii; extern const std::array DefaultMii; -extern const std::array RandomMiiFaceline; -extern const std::array RandomMiiFacelineColor; -extern const std::array RandomMiiFacelineWrinkle; -extern const std::array RandomMiiFacelineMakeup; -extern const std::array RandomMiiHairType; -extern const std::array RandomMiiHairColor; -extern const std::array RandomMiiEyeType; -extern const std::array RandomMiiEyeColor; -extern const std::array RandomMiiEyebrowType; -extern const std::array RandomMiiNoseType; -extern const std::array RandomMiiMouthType; -extern const std::array RandomMiiGlassType; + +extern const std::array EyeRotateLookup; +extern const std::array EyebrowRotateLookup; + +extern const std::array RandomMiiFaceline; +extern const std::array RandomMiiFacelineColor; +extern const std::array RandomMiiFacelineWrinkle; +extern const std::array RandomMiiFacelineMakeup; +extern const std::array RandomMiiHairType; +extern const std::array RandomMiiHairColor; +extern const std::array RandomMiiEyeType; +extern const std::array RandomMiiEyeColor; +extern const std::array RandomMiiEyebrowType; +extern const std::array RandomMiiNoseType; +extern const std::array RandomMiiMouthType; +extern const std::array 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); + +u8 GetFacelineColorFromVer3(u8 color); +u8 GetHairColorFromVer3(u32 color); +u8 GetEyeColorFromVer3(u32 color); +u8 GetMouthColorFromVer3(u32 color); +u8 GetGlassColorFromVer3(u8 color); } // namespace Service::Mii::RawData diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h index c48d08d794..cc8286189b 100644 --- a/src/core/hle/service/mii/types.h +++ b/src/core/hle/service/mii/types.h @@ -86,6 +86,8 @@ enum class SourceFlag : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); +using Nickname = std::array; + // nn::mii::CharInfo struct CharInfo { Common::UUID uuid; @@ -382,56 +384,10 @@ struct NfpStoreDataExtension { }; static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size"); -constexpr std::array Ver3FacelineColorTable{ - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, -}; - -constexpr std::array 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 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 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 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 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; MiiStoreData(); - MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields, + MiiStoreData(const Nickname& name, const MiiStoreBitFields& bit_fields, const Common::UUID& user_id); // This corresponds to the above structure MiiStoreBitFields. I did it like this because the @@ -441,7 +397,7 @@ struct MiiStoreData { std::array data{}; static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); - Name name{}; + Nickname name{}; Common::UUID uuid{}; } data; @@ -501,7 +457,7 @@ struct DefaultMii { u32 face_makeup{}; u32 hair_type{}; u32 hair_color{}; - u32 hair_flip{}; + HairFlip hair_flip{}; u32 eye_type{}; u32 eye_color{}; u32 eye_scale{}; @@ -524,8 +480,8 @@ struct DefaultMii { u32 mouth_scale{}; u32 mouth_aspect{}; u32 mouth_y{}; - u32 mustache_type{}; - u32 beard_type{}; + MustacheType mustache_type{}; + BeardType beard_type{}; u32 beard_color{}; u32 mustache_scale{}; u32 mustache_y{}; @@ -541,10 +497,10 @@ struct DefaultMii { u32 weight{}; Gender gender{}; u32 favorite_color{}; - u32 region{}; + u32 region_move{}; FontRegion font_region{}; u32 type{}; - INSERT_PADDING_WORDS(5); + Nickname nickname; }; static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); From 27929d7ca2b5fffc8866941d08cda921d586c45d Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 10 Sep 2023 21:58:18 -0600 Subject: [PATCH 21/69] service: mii: separate mii types into their own file --- src/core/CMakeLists.txt | 14 +- .../am/applets/applet_mii_edit_types.h | 3 +- src/core/hle/service/mii/mii.cpp | 2 +- src/core/hle/service/mii/mii_manager.cpp | 38 +- src/core/hle/service/mii/mii_manager.h | 16 +- src/core/hle/service/mii/mii_types.h | 157 ++++++ src/core/hle/service/mii/mii_util.h | 4 +- src/core/hle/service/mii/types.h | 509 ------------------ src/core/hle/service/mii/types/char_info.cpp | 6 + src/core/hle/service/mii/types/char_info.h | 76 +++ src/core/hle/service/mii/types/core_data.cpp | 6 + src/core/hle/service/mii/types/core_data.h | 105 ++++ .../hle/service/mii/{ => types}/raw_data.cpp | 2 +- .../hle/service/mii/{ => types}/raw_data.h | 2 +- src/core/hle/service/mii/types/store_data.cpp | 6 + src/core/hle/service/mii/types/store_data.h | 29 + .../hle/service/mii/types/ver3_store_data.cpp | 6 + .../hle/service/mii/types/ver3_store_data.h | 141 +++++ src/core/hle/service/nfc/common/device.cpp | 1 - src/core/hle/service/nfp/nfp_types.h | 5 +- 20 files changed, 586 insertions(+), 542 deletions(-) create mode 100644 src/core/hle/service/mii/mii_types.h delete mode 100644 src/core/hle/service/mii/types.h create mode 100644 src/core/hle/service/mii/types/char_info.cpp create mode 100644 src/core/hle/service/mii/types/char_info.h create mode 100644 src/core/hle/service/mii/types/core_data.cpp create mode 100644 src/core/hle/service/mii/types/core_data.h rename src/core/hle/service/mii/{ => types}/raw_data.cpp (99%) rename src/core/hle/service/mii/{ => types}/raw_data.h (98%) create mode 100644 src/core/hle/service/mii/types/store_data.cpp create mode 100644 src/core/hle/service/mii/types/store_data.h create mode 100644 src/core/hle/service/mii/types/ver3_store_data.cpp create mode 100644 src/core/hle/service/mii/types/ver3_store_data.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e9095ac529..c33910ade9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -584,15 +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/mii_result.h + hle/service/mii/mii_types.h hle/service/mii/mii_util.h - hle/service/mii/raw_data.cpp - hle/service/mii/raw_data.h - hle/service/mii/types.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h hle/service/mnpp/mnpp_app.cpp 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 4705d019fa..f3d7640732 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 bf3ee59073..680f06bebe 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -120,7 +120,7 @@ private: std::vector values; for (const auto& element : default_miis) { - values.emplace_back(element.info); + values.emplace_back(element.char_info); } ctx.WriteBuffer(SerializeArray(values)); diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index de70c3da6f..035eed5056 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -12,7 +12,8 @@ #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/mii_result.h" #include "core/hle/service/mii/mii_util.h" -#include "core/hle/service/mii/raw_data.h" +#include "core/hle/service/mii/types/core_data.h" +#include "core/hle/service/mii/types/raw_data.h" namespace Service::Mii { @@ -29,13 +30,12 @@ std::array ResizeArray(const std::array& i return out; } -CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) { - MiiStoreBitFields bf; - std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); +CharInfo ConvertStoreDataToInfo(const StoreData& data) { + const StoreDataBitFields& bf = data.core_data.data; return { - .uuid = data.data.uuid, - .name = ResizeArray(data.data.name), + .create_id = data.create_id, + .name = data.core_data.name, .font_region = static_cast(bf.font_region.Value()), .favorite_color = static_cast(bf.favorite_color.Value()), .gender = static_cast(bf.gender.Value()), @@ -89,8 +89,8 @@ CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) { }; } -MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { - MiiStoreBitFields bf{}; +StoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { + StoreDataBitFields bf{}; if (gender == Gender::All) { gender = MiiUtil::GetRandomValue(Gender::Maximum); @@ -270,8 +270,8 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo return {DefaultMiiName, bf, user_id}; } -MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { - MiiStoreBitFields bf{}; +StoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { + StoreDataBitFields bf{}; bf.font_region.Assign(info.font_region); bf.favorite_color.Assign(info.favorite_color); @@ -328,15 +328,15 @@ MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& u } // namespace -MiiStoreData::MiiStoreData() = default; +StoreData::StoreData() = default; -MiiStoreData::MiiStoreData(const Nickname& name, const MiiStoreBitFields& bit_fields, - const Common::UUID& user_id) { - data.name = name; - data.uuid = Common::UUID::MakeRandomRFC4122V4(); +StoreData::StoreData(const Nickname& name, const StoreDataBitFields& bit_fields, + const Common::UUID& user_id) { + core_data.name = name; + create_id = Common::UUID::MakeRandomRFC4122V4(); - std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); - data_crc = MiiUtil::CalculateCrc16(data.data.data(), sizeof(data)); + core_data.data = bit_fields; + data_crc = MiiUtil::CalculateCrc16(&core_data.data, sizeof(core_data.data)); device_crc = MiiUtil::CalculateCrc16(&user_id, sizeof(Common::UUID)); } @@ -641,8 +641,8 @@ bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { return is_valid; } -std::vector MiiManager::GetDefault(SourceFlag source_flag) { - std::vector result; +std::vector MiiManager::GetDefault(SourceFlag source_flag) { + std::vector result; if ((source_flag & SourceFlag::Default) == SourceFlag::None) { return result; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 0c8295ebe1..1f5c9e16fe 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -6,7 +6,10 @@ #include #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 { @@ -25,7 +28,7 @@ public: CharInfo BuildDefault(std::size_t index); CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; bool ValidateV3Info(const Ver3StoreData& mii_v3) const; - std::vector GetDefault(SourceFlag source_flag); + std::vector GetDefault(SourceFlag source_flag); Result GetIndex(const CharInfo& info, u32& index); // This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData @@ -34,6 +37,15 @@ public: // This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const; + struct MiiDatabase { + u32 magic{}; // 'NFDB' + std::array miis{}; + INSERT_PADDING_BYTES(1); + u8 count{}; + u16 crc{}; + }; + static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); + private: const Common::UUID user_id{}; u64 update_counter{}; 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 0000000000..ff836dcf24 --- /dev/null +++ b/src/core/hle/service/mii/mii_types.h @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#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); + +using Nickname = std::array; + +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"); + +struct DefaultMii { + u32 face_type{}; + u32 face_color{}; + u32 face_wrinkle{}; + u32 face_makeup{}; + u32 hair_type{}; + u32 hair_color{}; + HairFlip 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{}; + MustacheType mustache_type{}; + BeardType 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_move{}; + FontRegion font_region{}; + u32 type{}; + Nickname nickname; +}; +static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h index d98f838250..782ffe22ff 100644 --- a/src/core/hle/service/mii/mii_util.h +++ b/src/core/hle/service/mii/mii_util.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "common/uuid.h" -#include "core/hle/service/mii/types.h" +#include "core/hle/service/mii/mii_types.h" namespace Service::Mii { class MiiUtil { diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h deleted file mode 100644 index cc8286189b..0000000000 --- a/src/core/hle/service/mii/types.h +++ /dev/null @@ -1,509 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#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); - -using Nickname = std::array; - -// nn::mii::CharInfo -struct CharInfo { - Common::UUID uuid; - std::array 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, - "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 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 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 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 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"); - -struct MiiStoreData { - - MiiStoreData(); - MiiStoreData(const Nickname& 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 data{}; - static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); - - Nickname 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 miis{}; - INSERT_PADDING_BYTES(1); - u8 count{}; - u16 crc{}; -}; -static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); - -struct RandomMiiValues { - std::array values{}; -}; -static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); - -struct RandomMiiData4 { - Gender gender{}; - Age age{}; - Race race{}; - u32 values_count{}; - std::array values{}; -}; -static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); - -struct RandomMiiData3 { - u32 arg_1; - u32 arg_2; - u32 values_count; - std::array values{}; -}; -static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); - -struct RandomMiiData2 { - u32 arg_1; - u32 values_count; - std::array 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{}; - HairFlip 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{}; - MustacheType mustache_type{}; - BeardType 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_move{}; - FontRegion font_region{}; - u32 type{}; - Nickname nickname; -}; -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 0000000000..250fad44bc --- /dev/null +++ b/src/core/hle/service/mii/types/char_info.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/mii/types/char_info.h" + +namespace Service::Mii {} // 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 0000000000..5741b50892 --- /dev/null +++ b/src/core/hle/service/mii/types/char_info.h @@ -0,0 +1,76 @@ +// 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 { + +// This is nn::mii::detail::CharInfoRaw +struct CharInfo { + Common::UUID create_id; + Nickname name; + u16 null_terminator; + 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, + "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 0000000000..a7b12ad8dd --- /dev/null +++ b/src/core/hle/service/mii/types/core_data.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/mii/types/core_data.h" + +namespace Service::Mii {} // 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 0000000000..9dd7a53806 --- /dev/null +++ b/src/core/hle/service/mii/types/core_data.h @@ -0,0 +1,105 @@ +// 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, 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(StoreDataBitFields) == 0x1c, "StoreDataBitFields has incorrect size."); +static_assert(std::is_trivially_copyable_v, + "StoreDataBitFields is not trivially copyable."); + +struct CoreData { + 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 similarity index 99% rename from src/core/hle/service/mii/raw_data.cpp rename to src/core/hle/service/mii/types/raw_data.cpp index 070e2d199d..ef678c5276 100644 --- a/src/core/hle/service/mii/raw_data.cpp +++ b/src/core/hle/service/mii/types/raw_data.cpp @@ -1,7 +1,7 @@ // 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 { diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/types/raw_data.h similarity index 98% rename from src/core/hle/service/mii/raw_data.h rename to src/core/hle/service/mii/types/raw_data.h index ab84d09a1f..180f49fd0d 100644 --- a/src/core/hle/service/mii/raw_data.h +++ b/src/core/hle/service/mii/types/raw_data.h @@ -5,7 +5,7 @@ #include -#include "core/hle/service/mii/types.h" +#include "core/hle/service/mii/mii_types.h" 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 0000000000..aadc0e1afd --- /dev/null +++ b/src/core/hle/service/mii/types/store_data.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/mii/types/store_data.h" + +namespace Service::Mii {} // 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 0000000000..54a263b056 --- /dev/null +++ b/src/core/hle/service/mii/types/store_data.h @@ -0,0 +1,29 @@ +// 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 { + +struct StoreData { + StoreData(); + StoreData(const Nickname& name, const StoreDataBitFields& bit_fields, + const Common::UUID& user_id); + + 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 0000000000..4c8904c12c --- /dev/null +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/mii/types/ver3_store_data.h" + +namespace Service::Mii {} // 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 0000000000..c3963548c7 --- /dev/null +++ b/src/core/hle/service/mii/types/ver3_store_data.h @@ -0,0 +1,141 @@ +// 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" +#include "core/hle/service/mii/types/core_data.h" +#include "core/hle/service/mii/types/store_data.h" + +namespace Service::Mii { + +// 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 + +#pragma pack(push, 4) +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 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 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 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 5df40f9a0a..44ac8cc716 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" diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index aed12a7f8e..adcaa8e84b 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -6,7 +6,8 @@ #include #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/ver3_store_data.h" #include "core/hle/service/nfc/nfc_types.h" namespace Service::NFP { @@ -322,7 +323,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; From 8d7d62dc24b0788b6158fd6b3bd5bce6a6969a8c Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 10 Sep 2023 22:42:38 -0600 Subject: [PATCH 22/69] service: mii: Move ver3 operations --- src/core/hle/service/mii/mii_manager.cpp | 235 +----------------- src/core/hle/service/mii/mii_manager.h | 4 - src/core/hle/service/mii/types/char_info.h | 3 +- .../hle/service/mii/types/ver3_store_data.cpp | 224 ++++++++++++++++- .../hle/service/mii/types/ver3_store_data.h | 13 +- src/core/hle/service/nfc/common/device.cpp | 4 +- src/core/hle/service/nfp/nfp_types.h | 1 + 7 files changed, 241 insertions(+), 243 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 035eed5056..9ae7fb960d 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -394,173 +394,9 @@ CharInfo MiiManager::BuildDefault(std::size_t index) { } CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { - Service::Mii::MiiManager manager; - auto mii = manager.BuildBase(Mii::Gender::Male); - - if (!ValidateV3Info(mii_v3)) { - return mii; - } - - // TODO: We are ignoring a bunch of data from the mii_v3 - - mii.gender = static_cast(mii_v3.mii_information.gender); - mii.favorite_color = static_cast(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; - } - } - - 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(mii_v3.appearance_bits4.eye_type); - mii.eye_color = static_cast(mii_v3.appearance_bits4.eye_color); - mii.eye_scale = static_cast(mii_v3.appearance_bits4.eye_scale); - mii.eye_aspect = static_cast(mii_v3.appearance_bits4.eye_vertical_stretch); - mii.eye_rotate = static_cast(mii_v3.appearance_bits4.eye_rotation); - mii.eye_x = static_cast(mii_v3.appearance_bits4.eye_spacing); - mii.eye_y = static_cast(mii_v3.appearance_bits4.eye_y_position); - - mii.eyebrow_type = static_cast(mii_v3.appearance_bits5.eyebrow_style); - mii.eyebrow_color = static_cast(mii_v3.appearance_bits5.eyebrow_color); - mii.eyebrow_scale = static_cast(mii_v3.appearance_bits5.eyebrow_scale); - mii.eyebrow_aspect = static_cast(mii_v3.appearance_bits5.eyebrow_yscale); - mii.eyebrow_rotate = static_cast(mii_v3.appearance_bits5.eyebrow_rotation); - mii.eyebrow_x = static_cast(mii_v3.appearance_bits5.eyebrow_spacing); - mii.eyebrow_y = static_cast(mii_v3.appearance_bits5.eyebrow_y_position); - - mii.nose_type = static_cast(mii_v3.appearance_bits6.nose_type); - mii.nose_scale = static_cast(mii_v3.appearance_bits6.nose_scale); - mii.nose_y = static_cast(mii_v3.appearance_bits6.nose_y_position); - - mii.mouth_type = static_cast(mii_v3.appearance_bits7.mouth_type); - mii.mouth_color = static_cast(mii_v3.appearance_bits7.mouth_color); - mii.mouth_scale = static_cast(mii_v3.appearance_bits7.mouth_scale); - mii.mouth_aspect = static_cast(mii_v3.appearance_bits7.mouth_horizontal_stretch); - mii.mouth_y = static_cast(mii_v3.appearance_bits8.mouth_y_position); - - mii.mustache_type = static_cast(mii_v3.appearance_bits8.mustache_type); - mii.mustache_scale = static_cast(mii_v3.appearance_bits9.mustache_scale); - mii.mustache_y = static_cast(mii_v3.appearance_bits9.mustache_y_position); - - mii.beard_type = static_cast(mii_v3.appearance_bits9.bear_type); - mii.beard_color = static_cast(mii_v3.appearance_bits9.facial_hair_color); - - mii.glasses_type = static_cast(mii_v3.appearance_bits10.glasses_type); - mii.glasses_color = static_cast(mii_v3.appearance_bits10.glasses_color); - mii.glasses_scale = static_cast(mii_v3.appearance_bits10.glasses_scale); - mii.glasses_y = static_cast(mii_v3.appearance_bits10.glasses_y_position); - - mii.mole_type = static_cast(mii_v3.appearance_bits11.mole_enabled); - mii.mole_scale = static_cast(mii_v3.appearance_bits11.mole_scale); - mii.mole_x = static_cast(mii_v3.appearance_bits11.mole_x_position); - mii.mole_y = static_cast(mii_v3.appearance_bits11.mole_y_position); - - // TODO: Validate mii data - - return mii; -} - -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; - } - } - - 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); - - 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); - - 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); - - 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); - - 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); - - 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); - - mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); - - mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); - mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); - - 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); - - // These types are converted to V3 from a table - mii_v3.appearance_bits1.skin_color.Assign( - RawData::FromVer3GetFacelineColor(mii.faceline_color)); - mii_v3.appearance_bits3.hair_color.Assign(RawData::FromVer3GetHairColor(mii.hair_color)); - mii_v3.appearance_bits4.eye_color.Assign(RawData::FromVer3GetEyeColor(mii.eye_color)); - mii_v3.appearance_bits5.eyebrow_color.Assign(RawData::FromVer3GetHairColor(mii.eyebrow_color)); - mii_v3.appearance_bits7.mouth_color.Assign(RawData::FromVer3GetMouthlineColor(mii.mouth_color)); - mii_v3.appearance_bits9.facial_hair_color.Assign( - RawData::FromVer3GetHairColor(mii.beard_color)); - mii_v3.appearance_bits10.glasses_color.Assign( - RawData::FromVer3GetGlassColor(mii.glasses_color)); - mii_v3.appearance_bits10.glasses_type.Assign(RawData::FromVer3GetGlassType(mii.glasses_type)); - - mii_v3.crc = MiiUtil::CalculateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16)); - - // TODO: Validate mii_v3 data - - return mii_v3; + CharInfo char_info{}; + mii_v3.BuildToStoreData(char_info); + return char_info; } NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const { @@ -576,71 +412,6 @@ NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const { }; } -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; -} - std::vector MiiManager::GetDefault(SourceFlag source_flag) { std::vector result; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 1f5c9e16fe..0a47e613fb 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -27,13 +27,9 @@ public: 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 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; diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h index 5741b50892..cdebb1c9de 100644 --- a/src/core/hle/service/mii/types/char_info.h +++ b/src/core/hle/service/mii/types/char_info.h @@ -8,7 +8,8 @@ namespace Service::Mii { // This is nn::mii::detail::CharInfoRaw -struct CharInfo { +class CharInfo { +public: Common::UUID create_id; Nickname name; u16 null_terminator; diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index 4c8904c12c..c774f4b47a 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -1,6 +1,228 @@ // 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/char_info.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 {} // namespace Service::Mii +namespace Service::Mii { + +void Ver3StoreData::BuildToStoreData(CharInfo& out_char_info) const { + if (!IsValid()) { + return; + } + + // TODO: We are ignoring a bunch of data from the mii_v3 + + out_char_info.gender = static_cast(mii_information.gender); + out_char_info.favorite_color = static_cast(mii_information.favorite_color); + out_char_info.height = height; + out_char_info.build = build; + + // Copy name until string terminator + out_char_info.name = {}; + for (std::size_t index = 0; index < out_char_info.name.size() - 1; index++) { + out_char_info.name[index] = mii_name[index]; + if (out_char_info.name[index] == 0) { + break; + } + } + + out_char_info.font_region = region_information.character_set; + + out_char_info.faceline_type = appearance_bits1.face_shape; + out_char_info.faceline_color = appearance_bits1.skin_color; + out_char_info.faceline_wrinkle = appearance_bits2.wrinkles; + out_char_info.faceline_make = appearance_bits2.makeup; + + out_char_info.hair_type = hair_style; + out_char_info.hair_color = appearance_bits3.hair_color; + out_char_info.hair_flip = appearance_bits3.flip_hair; + + out_char_info.eye_type = static_cast(appearance_bits4.eye_type); + out_char_info.eye_color = static_cast(appearance_bits4.eye_color); + out_char_info.eye_scale = static_cast(appearance_bits4.eye_scale); + out_char_info.eye_aspect = static_cast(appearance_bits4.eye_vertical_stretch); + out_char_info.eye_rotate = static_cast(appearance_bits4.eye_rotation); + out_char_info.eye_x = static_cast(appearance_bits4.eye_spacing); + out_char_info.eye_y = static_cast(appearance_bits4.eye_y_position); + + out_char_info.eyebrow_type = static_cast(appearance_bits5.eyebrow_style); + out_char_info.eyebrow_color = static_cast(appearance_bits5.eyebrow_color); + out_char_info.eyebrow_scale = static_cast(appearance_bits5.eyebrow_scale); + out_char_info.eyebrow_aspect = static_cast(appearance_bits5.eyebrow_yscale); + out_char_info.eyebrow_rotate = static_cast(appearance_bits5.eyebrow_rotation); + out_char_info.eyebrow_x = static_cast(appearance_bits5.eyebrow_spacing); + out_char_info.eyebrow_y = static_cast(appearance_bits5.eyebrow_y_position); + + out_char_info.nose_type = static_cast(appearance_bits6.nose_type); + out_char_info.nose_scale = static_cast(appearance_bits6.nose_scale); + out_char_info.nose_y = static_cast(appearance_bits6.nose_y_position); + + out_char_info.mouth_type = static_cast(appearance_bits7.mouth_type); + out_char_info.mouth_color = static_cast(appearance_bits7.mouth_color); + out_char_info.mouth_scale = static_cast(appearance_bits7.mouth_scale); + out_char_info.mouth_aspect = static_cast(appearance_bits7.mouth_horizontal_stretch); + out_char_info.mouth_y = static_cast(appearance_bits8.mouth_y_position); + + out_char_info.mustache_type = static_cast(appearance_bits8.mustache_type); + out_char_info.mustache_scale = static_cast(appearance_bits9.mustache_scale); + out_char_info.mustache_y = static_cast(appearance_bits9.mustache_y_position); + + out_char_info.beard_type = static_cast(appearance_bits9.bear_type); + out_char_info.beard_color = static_cast(appearance_bits9.facial_hair_color); + + out_char_info.glasses_type = static_cast(appearance_bits10.glasses_type); + out_char_info.glasses_color = static_cast(appearance_bits10.glasses_color); + out_char_info.glasses_scale = static_cast(appearance_bits10.glasses_scale); + out_char_info.glasses_y = static_cast(appearance_bits10.glasses_y_position); + + out_char_info.mole_type = static_cast(appearance_bits11.mole_enabled); + out_char_info.mole_scale = static_cast(appearance_bits11.mole_scale); + out_char_info.mole_x = static_cast(appearance_bits11.mole_x_position); + out_char_info.mole_y = static_cast(appearance_bits11.mole_y_position); +} + +void Ver3StoreData::BuildFromStoreData(const CharInfo& char_info) { + version = 1; + mii_information.gender.Assign(char_info.gender); + mii_information.favorite_color.Assign(char_info.favorite_color); + height = char_info.height; + build = char_info.build; + + // Copy name until string terminator + mii_name = {}; + for (std::size_t index = 0; index < char_info.name.size() - 1; index++) { + mii_name[index] = char_info.name[index]; + if (mii_name[index] == 0) { + break; + } + } + + region_information.character_set.Assign(char_info.font_region); + + appearance_bits1.face_shape.Assign(char_info.faceline_type); + appearance_bits2.wrinkles.Assign(char_info.faceline_wrinkle); + appearance_bits2.makeup.Assign(char_info.faceline_make); + + hair_style = char_info.hair_type; + appearance_bits3.flip_hair.Assign(char_info.hair_flip); + + appearance_bits4.eye_type.Assign(char_info.eye_type); + appearance_bits4.eye_scale.Assign(char_info.eye_scale); + appearance_bits4.eye_vertical_stretch.Assign(char_info.eye_aspect); + appearance_bits4.eye_rotation.Assign(char_info.eye_rotate); + appearance_bits4.eye_spacing.Assign(char_info.eye_x); + appearance_bits4.eye_y_position.Assign(char_info.eye_y); + + appearance_bits5.eyebrow_style.Assign(char_info.eyebrow_type); + appearance_bits5.eyebrow_scale.Assign(char_info.eyebrow_scale); + appearance_bits5.eyebrow_yscale.Assign(char_info.eyebrow_aspect); + appearance_bits5.eyebrow_rotation.Assign(char_info.eyebrow_rotate); + appearance_bits5.eyebrow_spacing.Assign(char_info.eyebrow_x); + appearance_bits5.eyebrow_y_position.Assign(char_info.eyebrow_y); + + appearance_bits6.nose_type.Assign(char_info.nose_type); + appearance_bits6.nose_scale.Assign(char_info.nose_scale); + appearance_bits6.nose_y_position.Assign(char_info.nose_y); + + appearance_bits7.mouth_type.Assign(char_info.mouth_type); + appearance_bits7.mouth_scale.Assign(char_info.mouth_scale); + appearance_bits7.mouth_horizontal_stretch.Assign(char_info.mouth_aspect); + appearance_bits8.mouth_y_position.Assign(char_info.mouth_y); + + appearance_bits8.mustache_type.Assign(char_info.mustache_type); + appearance_bits9.mustache_scale.Assign(char_info.mustache_scale); + appearance_bits9.mustache_y_position.Assign(char_info.mustache_y); + + appearance_bits9.bear_type.Assign(char_info.beard_type); + + appearance_bits10.glasses_scale.Assign(char_info.glasses_scale); + appearance_bits10.glasses_y_position.Assign(char_info.glasses_y); + + appearance_bits11.mole_enabled.Assign(char_info.mole_type); + appearance_bits11.mole_scale.Assign(char_info.mole_scale); + appearance_bits11.mole_x_position.Assign(char_info.mole_x); + appearance_bits11.mole_y_position.Assign(char_info.mole_y); + + // These types are converted to V3 from a table + appearance_bits1.skin_color.Assign(RawData::FromVer3GetFacelineColor(char_info.faceline_color)); + appearance_bits3.hair_color.Assign(RawData::FromVer3GetHairColor(char_info.hair_color)); + appearance_bits4.eye_color.Assign(RawData::FromVer3GetEyeColor(char_info.eye_color)); + appearance_bits5.eyebrow_color.Assign(RawData::FromVer3GetHairColor(char_info.eyebrow_color)); + appearance_bits7.mouth_color.Assign(RawData::FromVer3GetMouthlineColor(char_info.mouth_color)); + appearance_bits9.facial_hair_color.Assign(RawData::FromVer3GetHairColor(char_info.beard_color)); + appearance_bits10.glasses_color.Assign(RawData::FromVer3GetGlassColor(char_info.glasses_color)); + appearance_bits10.glasses_type.Assign(RawData::FromVer3GetGlassType(char_info.glasses_type)); + + 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[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 < 12); + is_valid = is_valid && (height < 128); + is_valid = is_valid && (build < 128); + + is_valid = is_valid && (appearance_bits1.face_shape < 12); + is_valid = is_valid && (appearance_bits1.skin_color < 7); + is_valid = is_valid && (appearance_bits2.wrinkles < 12); + is_valid = is_valid && (appearance_bits2.makeup < 12); + + is_valid = is_valid && (hair_style < 132); + is_valid = is_valid && (appearance_bits3.hair_color < 8); + + is_valid = is_valid && (appearance_bits4.eye_type < 60); + is_valid = is_valid && (appearance_bits4.eye_color < 6); + is_valid = is_valid && (appearance_bits4.eye_scale < 8); + is_valid = is_valid && (appearance_bits4.eye_vertical_stretch < 7); + is_valid = is_valid && (appearance_bits4.eye_rotation < 8); + is_valid = is_valid && (appearance_bits4.eye_spacing < 13); + is_valid = is_valid && (appearance_bits4.eye_y_position < 19); + + is_valid = is_valid && (appearance_bits5.eyebrow_style < 25); + is_valid = is_valid && (appearance_bits5.eyebrow_color < 8); + is_valid = is_valid && (appearance_bits5.eyebrow_scale < 9); + is_valid = is_valid && (appearance_bits5.eyebrow_yscale < 7); + is_valid = is_valid && (appearance_bits5.eyebrow_rotation < 12); + is_valid = is_valid && (appearance_bits5.eyebrow_spacing < 12); + is_valid = is_valid && (appearance_bits5.eyebrow_y_position < 19); + + is_valid = is_valid && (appearance_bits6.nose_type < 18); + is_valid = is_valid && (appearance_bits6.nose_scale < 9); + is_valid = is_valid && (appearance_bits6.nose_y_position < 19); + + is_valid = is_valid && (appearance_bits7.mouth_type < 36); + is_valid = is_valid && (appearance_bits7.mouth_color < 5); + is_valid = is_valid && (appearance_bits7.mouth_scale < 9); + is_valid = is_valid && (appearance_bits7.mouth_horizontal_stretch < 7); + is_valid = is_valid && (appearance_bits8.mouth_y_position < 19); + + is_valid = is_valid && (appearance_bits8.mustache_type < 6); + is_valid = is_valid && (appearance_bits9.mustache_scale < 7); + is_valid = is_valid && (appearance_bits9.mustache_y_position < 17); + + is_valid = is_valid && (appearance_bits9.bear_type < 6); + is_valid = is_valid && (appearance_bits9.facial_hair_color < 8); + + is_valid = is_valid && (appearance_bits10.glasses_type < 9); + is_valid = is_valid && (appearance_bits10.glasses_color < 6); + is_valid = is_valid && (appearance_bits10.glasses_scale < 8); + is_valid = is_valid && (appearance_bits10.glasses_y_position < 21); + + is_valid = is_valid && (appearance_bits11.mole_enabled < 2); + is_valid = is_valid && (appearance_bits11.mole_scale < 9); + is_valid = is_valid && (appearance_bits11.mole_x_position < 17); + is_valid = is_valid && (appearance_bits11.mole_y_position < 31); + + 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 index c3963548c7..6b4e1eb9cd 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.h +++ b/src/core/hle/service/mii/types/ver3_store_data.h @@ -4,10 +4,9 @@ #pragma once #include "core/hle/service/mii/mii_types.h" -#include "core/hle/service/mii/types/core_data.h" -#include "core/hle/service/mii/types/store_data.h" namespace Service::Mii { +class CharInfo; // This is nn::mii::Ver3StoreData // Based on citra HLE::Applets::MiiData and PretendoNetwork. @@ -15,7 +14,15 @@ namespace Service::Mii { // https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299 #pragma pack(push, 4) -struct Ver3StoreData { +class Ver3StoreData { +public: + // TODO: This function is wrong. It should use StoreData. + void BuildToStoreData(CharInfo& out_char_info) const; + // TODO: This function is wrong. It should use StoreData. + void BuildFromStoreData(const CharInfo& char_info); + + u32 IsValid() const; + u8 version; union { u8 raw; diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 44ac8cc716..5e61d2595e 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -834,7 +834,7 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe } SetAmiiboName(settings, register_info.amiibo_name); - tag_data.owner_mii = manager.BuildFromStoreData(mii); + tag_data.owner_mii.BuildFromStoreData(mii); tag_data.mii_extension = manager.SetFromStoreData(mii); tag_data.unknown = 0; tag_data.unknown2 = {}; @@ -1466,7 +1466,7 @@ 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)); + stubbed_tag_data.owner_mii.BuildFromStoreData(manager.BuildBase(Mii::Gender::Male)); // 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 adcaa8e84b..f96d212205 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -7,6 +7,7 @@ #include "common/swap.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" From 81f50d51326fc1ce401bd21c28cf78371b4ddcea Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 10 Sep 2023 22:52:33 -0600 Subject: [PATCH 23/69] service: mii: Move core data operations --- src/core/hle/service/mii/mii_manager.cpp | 247 +------- src/core/hle/service/mii/types/core_data.cpp | 597 +++++++++++++++++- src/core/hle/service/mii/types/core_data.h | 111 +++- src/core/hle/service/mii/types/store_data.cpp | 16 +- src/core/hle/service/mii/types/store_data.h | 3 +- 5 files changed, 729 insertions(+), 245 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 9ae7fb960d..3483d95e54 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -90,256 +90,21 @@ CharInfo ConvertStoreDataToInfo(const StoreData& data) { } StoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { - StoreDataBitFields bf{}; + CoreData core_data{}; + core_data.BuildRandom(age, gender, race); - if (gender == Gender::All) { - gender = MiiUtil::GetRandomValue(Gender::Maximum); - } - - bf.gender.Assign(gender); - bf.favorite_color.Assign(MiiUtil::GetRandomValue(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{MiiUtil::GetRandomValue(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{MiiUtil::GetRandomValue(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 = MiiUtil::GetRandomValue(3); - } - - const std::size_t index{3 * static_cast(age) + - 9 * static_cast(gender) + static_cast(race)}; - - const auto& faceline_type_info{RawData::RandomMiiFaceline.at(index)}; - const auto& faceline_color_info{RawData::RandomMiiFacelineColor.at( - 3 * static_cast(gender) + static_cast(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(race) + - static_cast(age))}; - const auto& eye_type_info{RawData::RandomMiiEyeType.at(index)}; - const auto& eye_color_info{RawData::RandomMiiEyeColor.at(static_cast(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(age))}; - - bf.faceline_type.Assign( - faceline_type_info - .values[MiiUtil::GetRandomValue(faceline_type_info.values_count)]); - bf.faceline_color.Assign( - faceline_color_info - .values[MiiUtil::GetRandomValue(faceline_color_info.values_count)]); - bf.faceline_wrinkle.Assign( - faceline_wrinkle_info - .values[MiiUtil::GetRandomValue(faceline_wrinkle_info.values_count)]); - bf.faceline_makeup.Assign( - faceline_makeup_info - .values[MiiUtil::GetRandomValue(faceline_makeup_info.values_count)]); - - bf.hair_type.Assign( - hair_type_info.values[MiiUtil::GetRandomValue(hair_type_info.values_count)]); - bf.hair_color.Assign(RawData::GetHairColorFromVer3( - hair_color_info - .values[MiiUtil::GetRandomValue(hair_color_info.values_count)])); - bf.hair_flip.Assign(MiiUtil::GetRandomValue(HairFlip::Maximum)); - - bf.eye_type.Assign( - eye_type_info.values[MiiUtil::GetRandomValue(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[bf.eye_type]}; - - bf.eye_color.Assign(RawData::GetEyeColorFromVer3( - eye_color_info.values[MiiUtil::GetRandomValue(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[MiiUtil::GetRandomValue(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(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[MiiUtil::GetRandomValue(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 ? MiiUtil::GetRandomValue(4) : 0}; - - bf.mouth_type.Assign( - mouth_type_info.values[MiiUtil::GetRandomValue(mouth_type_info.values_count)]); - bf.mouth_color.Assign(RawData::GetMouthColorFromVer3(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 && MiiUtil::GetRandomValue(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::Beard1, BeardType::Beard5); - } - - if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == - BeardAndMustacheFlag::Mustache) { - mustache_type = MiiUtil::GetRandomValue(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{MiiUtil::GetRandomValue(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(RawData::GetGlassColorFromVer3(0)); - bf.glasses_scale.Assign(4); - bf.glasses_y.Assign(axis_y + 10); - - bf.mole_type.Assign(0); - bf.mole_scale.Assign(4); - bf.mole_x.Assign(2); - bf.mole_y.Assign(20); - - return {DefaultMiiName, bf, user_id}; + return {DefaultMiiName, core_data.data, user_id}; } StoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { - StoreDataBitFields bf{}; + CoreData core_data{}; + core_data.SetDefault(); - 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_move); - 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(RawData::GetHairColorFromVer3(info.hair_color)); - bf.hair_flip.Assign(static_cast(info.hair_flip)); - bf.eye_type.Assign(info.eye_type); - bf.eye_color.Assign(RawData::GetEyeColorFromVer3(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(RawData::GetHairColorFromVer3(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(RawData::GetMouthColorFromVer3(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(RawData::GetHairColorFromVer3(info.beard_color)); - bf.beard_type.Assign(static_cast(info.beard_type)); - bf.mustache_type.Assign(static_cast(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(RawData::GetGlassColorFromVer3(static_cast(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}; + return {DefaultMiiName, core_data.data, user_id}; } } // namespace -StoreData::StoreData() = default; - -StoreData::StoreData(const Nickname& name, const StoreDataBitFields& bit_fields, - const Common::UUID& user_id) { - core_data.name = name; - create_id = Common::UUID::MakeRandomRFC4122V4(); - - core_data.data = bit_fields; - data_crc = MiiUtil::CalculateCrc16(&core_data.data, sizeof(core_data.data)); - device_crc = MiiUtil::CalculateCrc16(&user_id, sizeof(Common::UUID)); -} - MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {} bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) { diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp index a7b12ad8dd..76c57fff2e 100644 --- a/src/core/hle/service/mii/types/core_data.cpp +++ b/src/core/hle/service/mii/types/core_data.cpp @@ -1,6 +1,601 @@ // 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/core_data.h" +#include "core/hle/service/mii/types/raw_data.h" -namespace Service::Mii {} // namespace Service::Mii +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::Maximum); + } + + data.gender.Assign(gender); + data.favorite_color.Assign(MiiUtil::GetRandomValue(11)); + data.region_move.Assign(0); + data.font_region.Assign(FontRegion::Standard); + data.type.Assign(0); + data.height.Assign(64); + data.build.Assign(64); + + if (age == Age::All) { + const auto temp{MiiUtil::GetRandomValue(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{MiiUtil::GetRandomValue(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 = MiiUtil::GetRandomValue(3); + } + + const std::size_t index{3 * static_cast(age) + + 9 * static_cast(gender) + static_cast(race)}; + + const auto& faceline_type_info{RawData::RandomMiiFaceline.at(index)}; + const auto& faceline_color_info{RawData::RandomMiiFacelineColor.at( + 3 * static_cast(gender) + static_cast(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(race) + + static_cast(age))}; + const auto& eye_type_info{RawData::RandomMiiEyeType.at(index)}; + const auto& eye_color_info{RawData::RandomMiiEyeColor.at(static_cast(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(age))}; + + data.faceline_type.Assign( + faceline_type_info + .values[MiiUtil::GetRandomValue(faceline_type_info.values_count)]); + data.faceline_color.Assign( + faceline_color_info + .values[MiiUtil::GetRandomValue(faceline_color_info.values_count)]); + data.faceline_wrinkle.Assign( + faceline_wrinkle_info + .values[MiiUtil::GetRandomValue(faceline_wrinkle_info.values_count)]); + data.faceline_makeup.Assign( + faceline_makeup_info + .values[MiiUtil::GetRandomValue(faceline_makeup_info.values_count)]); + + data.hair_type.Assign( + hair_type_info.values[MiiUtil::GetRandomValue(hair_type_info.values_count)]); + data.hair_color.Assign(RawData::GetHairColorFromVer3( + hair_color_info + .values[MiiUtil::GetRandomValue(hair_color_info.values_count)])); + data.hair_flip.Assign(MiiUtil::GetRandomValue(HairFlip::Maximum)); + + data.eye_type.Assign( + eye_type_info.values[MiiUtil::GetRandomValue(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]}; + + data.eye_color.Assign(RawData::GetEyeColorFromVer3( + eye_color_info.values[MiiUtil::GetRandomValue(eye_color_info.values_count)])); + data.eye_scale.Assign(4); + data.eye_aspect.Assign(3); + data.eye_rotate.Assign(eye_rotate_offset - eye_rotate); + data.eye_x.Assign(2); + data.eye_y.Assign(axis_y + 12); + + data.eyebrow_type.Assign( + eyebrow_type_info + .values[MiiUtil::GetRandomValue(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(data.eyebrow_type.Value())]}; + + data.eyebrow_color.Assign(data.hair_color); + data.eyebrow_scale.Assign(4); + data.eyebrow_aspect.Assign(3); + data.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate); + data.eyebrow_x.Assign(2); + data.eyebrow_y.Assign(axis_y + eyebrow_y); + + const auto nose_scale{gender == Gender::Female ? 3 : 4}; + + data.nose_type.Assign( + nose_type_info.values[MiiUtil::GetRandomValue(nose_type_info.values_count)]); + data.nose_scale.Assign(nose_scale); + data.nose_y.Assign(axis_y + 9); + + const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue(4) : 0}; + + data.mouth_type.Assign( + mouth_type_info.values[MiiUtil::GetRandomValue(mouth_type_info.values_count)]); + data.mouth_color.Assign(RawData::GetMouthColorFromVer3(mouth_color)); + data.mouth_scale.Assign(4); + data.mouth_aspect.Assign(3); + data.mouth_y.Assign(axis_y + 13); + + data.beard_color.Assign(data.hair_color); + data.mustache_scale.Assign(4); + + if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue(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::Beard1, BeardType::Beard5); + } + + if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == + BeardAndMustacheFlag::Mustache) { + mustache_type = MiiUtil::GetRandomValue(MustacheType::Mustache1, + MustacheType::Mustache5); + } + + data.mustache_type.Assign(mustache_type); + data.beard_type.Assign(beard_type); + data.mustache_y.Assign(10); + } else { + data.mustache_type.Assign(MustacheType::None); + data.beard_type.Assign(BeardType::None); + data.mustache_y.Assign(axis_y + 10); + } + + const auto glasses_type_start{MiiUtil::GetRandomValue(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; + } + } + + data.glasses_type.Assign(glasses_type); + data.glasses_color.Assign(RawData::GetGlassColorFromVer3(0)); + data.glasses_scale.Assign(4); + data.glasses_y.Assign(axis_y + 10); + + data.mole_type.Assign(0); + data.mole_scale.Assign(4); + data.mole_x.Assign(2); + data.mole_y.Assign(20); +} + +u32 CoreData::IsValid() const { + // TODO: Complete this + return 0; +} + +void CoreData::SetFontRegion(FontRegion value) { + data.font_region.Assign(value); +} + +void CoreData::SetFavoriteColor(u8 value) { + data.favorite_color.Assign(value); +} + +void CoreData::SetGender(Gender value) { + data.gender.Assign(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(u8 value) { + data.faceline_type.Assign(value); +} + +void CoreData::SetFacelineColor(u8 value) { + data.faceline_color.Assign(value); +} + +void CoreData::SetFacelineWrinkle(u8 value) { + data.faceline_wrinkle.Assign(value); +} + +void CoreData::SetFacelineMake(u8 value) { + data.faceline_makeup.Assign(value); +} + +void CoreData::SetHairType(u8 value) { + data.hair_type.Assign(value); +} + +void CoreData::SetHairColor(u8 value) { + data.hair_color.Assign(value); +} + +void CoreData::SetHairFlip(HairFlip value) { + data.hair_flip.Assign(value); +} + +void CoreData::SetEyeType(u8 value) { + data.eye_type.Assign(value); +} + +void CoreData::SetEyeColor(u8 value) { + data.eye_color.Assign(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(u8 value) { + data.eyebrow_type.Assign(value); +} + +void CoreData::SetEyebrowColor(u8 value) { + data.eyebrow_color.Assign(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(u8 value) { + data.nose_type.Assign(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(u8 value) { + data.mouth_color.Assign(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(u8 value) { + data.beard_color.Assign(value); +} + +void CoreData::SetBeardType(BeardType value) { + data.beard_type.Assign(value); +} + +void CoreData::SetMustacheType(MustacheType value) { + data.mustache_type.Assign(value); +} + +void CoreData::SetMustacheScale(u8 value) { + data.mustache_scale.Assign(value); +} + +void CoreData::SetMustacheY(u8 value) { + data.mustache_y.Assign(value); +} + +void CoreData::SetGlassType(u8 value) { + data.glasses_type.Assign(value); +} + +void CoreData::SetGlassColor(u8 value) { + data.glasses_color.Assign(value); +} + +void CoreData::SetGlassScale(u8 value) { + data.glasses_scale.Assign(value); +} + +void CoreData::SetGlassY(u8 value) { + data.glasses_y.Assign(value); +} + +void CoreData::SetMoleType(u8 value) { + data.mole_type.Assign(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; +} + +u8 CoreData::GetFavoriteColor() const { + return static_cast(data.favorite_color.Value()); +} + +u8 CoreData::GetGender() const { + return static_cast(data.gender.Value()); +} + +u8 CoreData::GetHeight() const { + return static_cast(data.height.Value()); +} + +u8 CoreData::GetBuild() const { + return static_cast(data.build.Value()); +} + +u8 CoreData::GetType() const { + return static_cast(data.type.Value()); +} + +u8 CoreData::GetRegionMove() const { + return static_cast(data.region_move.Value()); +} + +u8 CoreData::GetFacelineType() const { + return static_cast(data.faceline_type.Value()); +} + +u8 CoreData::GetFacelineColor() const { + return static_cast(data.faceline_color.Value()); +} + +u8 CoreData::GetFacelineWrinkle() const { + return static_cast(data.faceline_wrinkle.Value()); +} + +u8 CoreData::GetFacelineMake() const { + return static_cast(data.faceline_makeup.Value()); +} + +u8 CoreData::GetHairType() const { + return static_cast(data.hair_type.Value()); +} + +u8 CoreData::GetHairColor() const { + return static_cast(data.hair_color.Value()); +} + +u8 CoreData::GetHairFlip() const { + return static_cast(data.hair_flip.Value()); +} + +u8 CoreData::GetEyeType() const { + return static_cast(data.eye_type.Value()); +} + +u8 CoreData::GetEyeColor() const { + return static_cast(data.eye_color.Value()); +} + +u8 CoreData::GetEyeScale() const { + return static_cast(data.eye_scale.Value()); +} + +u8 CoreData::GetEyeAspect() const { + return static_cast(data.eye_aspect.Value()); +} + +u8 CoreData::GetEyeRotate() const { + return static_cast(data.eye_rotate.Value()); +} + +u8 CoreData::GetEyeX() const { + return static_cast(data.eye_x.Value()); +} + +u8 CoreData::GetEyeY() const { + return static_cast(data.eye_y.Value()); +} + +u8 CoreData::GetEyebrowType() const { + return static_cast(data.eyebrow_type.Value()); +} + +u8 CoreData::GetEyebrowColor() const { + return static_cast(data.eyebrow_color.Value()); +} + +u8 CoreData::GetEyebrowScale() const { + return static_cast(data.eyebrow_scale.Value()); +} + +u8 CoreData::GetEyebrowAspect() const { + return static_cast(data.eyebrow_aspect.Value()); +} + +u8 CoreData::GetEyebrowRotate() const { + return static_cast(data.eyebrow_rotate.Value()); +} + +u8 CoreData::GetEyebrowX() const { + return static_cast(data.eyebrow_x.Value()); +} + +u8 CoreData::GetEyebrowY() const { + return static_cast(data.eyebrow_y.Value()); +} + +u8 CoreData::GetNoseType() const { + return static_cast(data.nose_type.Value()); +} + +u8 CoreData::GetNoseScale() const { + return static_cast(data.nose_scale.Value()); +} + +u8 CoreData::GetNoseY() const { + return static_cast(data.nose_y.Value()); +} + +u8 CoreData::GetMouthType() const { + return static_cast(data.mouth_type.Value()); +} + +u8 CoreData::GetMouthColor() const { + return static_cast(data.mouth_color.Value()); +} + +u8 CoreData::GetMouthScale() const { + return static_cast(data.mouth_scale.Value()); +} + +u8 CoreData::GetMouthAspect() const { + return static_cast(data.mouth_aspect.Value()); +} + +u8 CoreData::GetMouthY() const { + return static_cast(data.mouth_y.Value()); +} + +u8 CoreData::GetBeardColor() const { + return static_cast(data.beard_color.Value()); +} + +u8 CoreData::GetBeardType() const { + return static_cast(data.beard_type.Value()); +} + +u8 CoreData::GetMustacheType() const { + return static_cast(data.mustache_type.Value()); +} + +u8 CoreData::GetMustacheScale() const { + return static_cast(data.mustache_scale.Value()); +} + +u8 CoreData::GetMustacheY() const { + return static_cast(data.mustache_y.Value()); +} + +u8 CoreData::GetGlassType() const { + return static_cast(data.glasses_type.Value()); +} + +u8 CoreData::GetGlassColor() const { + return static_cast(data.glasses_color.Value()); +} + +u8 CoreData::GetGlassScale() const { + return static_cast(data.glasses_scale.Value()); +} + +u8 CoreData::GetGlassY() const { + return static_cast(data.glasses_y.Value()); +} + +u8 CoreData::GetMoleType() const { + return static_cast(data.mole_type.Value()); +} + +u8 CoreData::GetMoleScale() const { + return static_cast(data.mole_scale.Value()); +} + +u8 CoreData::GetMoleX() const { + return static_cast(data.mole_x.Value()); +} + +u8 CoreData::GetMoleY() const { + return static_cast(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' ', 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 index 9dd7a53806..76daf4e6e8 100644 --- a/src/core/hle/service/mii/types/core_data.h +++ b/src/core/hle/service/mii/types/core_data.h @@ -96,7 +96,116 @@ static_assert(sizeof(StoreDataBitFields) == 0x1c, "StoreDataBitFields has incorr static_assert(std::is_trivially_copyable_v, "StoreDataBitFields is not trivially copyable."); -struct CoreData { +class CoreData { +public: + void SetDefault(); + void BuildRandom(Age age, Gender gender, Race race); + + u32 IsValid() const; + + void SetFontRegion(FontRegion value); + void SetFavoriteColor(u8 value); + void SetGender(Gender value); + void SetHeight(u8 value); + void SetBuild(u8 value); + void SetType(u8 value); + void SetRegionMove(u8 value); + void SetFacelineType(u8 value); + void SetFacelineColor(u8 value); + void SetFacelineWrinkle(u8 value); + void SetFacelineMake(u8 value); + void SetHairType(u8 value); + void SetHairColor(u8 value); + void SetHairFlip(HairFlip value); + void SetEyeType(u8 value); + void SetEyeColor(u8 value); + void SetEyeScale(u8 value); + void SetEyeAspect(u8 value); + void SetEyeRotate(u8 value); + void SetEyeX(u8 value); + void SetEyeY(u8 value); + void SetEyebrowType(u8 value); + void SetEyebrowColor(u8 value); + void SetEyebrowScale(u8 value); + void SetEyebrowAspect(u8 value); + void SetEyebrowRotate(u8 value); + void SetEyebrowX(u8 value); + void SetEyebrowY(u8 value); + void SetNoseType(u8 value); + void SetNoseScale(u8 value); + void SetNoseY(u8 value); + void SetMouthType(u8 value); + void SetMouthColor(u8 value); + void SetMouthScale(u8 value); + void SetMouthAspect(u8 value); + void SetMouthY(u8 value); + void SetBeardColor(u8 value); + void SetBeardType(BeardType value); + void SetMustacheType(MustacheType value); + void SetMustacheScale(u8 value); + void SetMustacheY(u8 value); + void SetGlassType(u8 value); + void SetGlassColor(u8 value); + void SetGlassScale(u8 value); + void SetGlassY(u8 value); + void SetMoleType(u8 value); + void SetMoleScale(u8 value); + void SetMoleX(u8 value); + void SetMoleY(u8 value); + void SetNickname(Nickname nickname); + + u8 GetFavoriteColor() const; + u8 GetGender() const; + u8 GetHeight() const; + u8 GetBuild() const; + u8 GetType() const; + u8 GetRegionMove() const; + u8 GetFacelineType() const; + u8 GetFacelineColor() const; + u8 GetFacelineWrinkle() const; + u8 GetFacelineMake() const; + u8 GetHairType() const; + u8 GetHairColor() const; + u8 GetHairFlip() const; + u8 GetEyeType() const; + u8 GetEyeColor() const; + u8 GetEyeScale() const; + u8 GetEyeAspect() const; + u8 GetEyeRotate() const; + u8 GetEyeX() const; + u8 GetEyeY() const; + u8 GetEyebrowType() const; + u8 GetEyebrowColor() const; + u8 GetEyebrowScale() const; + u8 GetEyebrowAspect() const; + u8 GetEyebrowRotate() const; + u8 GetEyebrowX() const; + u8 GetEyebrowY() const; + u8 GetNoseType() const; + u8 GetNoseScale() const; + u8 GetNoseY() const; + u8 GetMouthType() const; + u8 GetMouthColor() const; + u8 GetMouthScale() const; + u8 GetMouthAspect() const; + u8 GetMouthY() const; + u8 GetBeardColor() const; + u8 GetBeardType() const; + u8 GetMustacheType() const; + u8 GetMustacheScale() const; + u8 GetMustacheY() const; + u8 GetGlassType() const; + u8 GetGlassColor() const; + u8 GetGlassScale() const; + u8 GetGlassY() const; + u8 GetMoleType() const; + u8 GetMoleScale() const; + u8 GetMoleX() const; + u8 GetMoleY() const; + Nickname GetNickname() const; + Nickname GetDefaultNickname() const; + Nickname GetInvalidNickname() const; + StoreDataBitFields data{}; Nickname name{}; }; diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp index aadc0e1afd..459e8699fc 100644 --- a/src/core/hle/service/mii/types/store_data.cpp +++ b/src/core/hle/service/mii/types/store_data.cpp @@ -1,6 +1,20 @@ // 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/store_data.h" -namespace Service::Mii {} // namespace Service::Mii +namespace Service::Mii { +StoreData::StoreData() = default; + +StoreData::StoreData(const Nickname& name, const StoreDataBitFields& bit_fields, + const Common::UUID& user_id) { + core_data.name = name; + create_id = Common::UUID::MakeRandomRFC4122V4(); + + core_data.data = bit_fields; + data_crc = MiiUtil::CalculateCrc16(&core_data.data, sizeof(core_data.data)); + device_crc = MiiUtil::CalculateCrc16(&user_id, sizeof(Common::UUID)); +} + +} // 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 index 54a263b056..be69509672 100644 --- a/src/core/hle/service/mii/types/store_data.h +++ b/src/core/hle/service/mii/types/store_data.h @@ -8,7 +8,8 @@ namespace Service::Mii { -struct StoreData { +class StoreData { +public: StoreData(); StoreData(const Nickname& name, const StoreDataBitFields& bit_fields, const Common::UUID& user_id); From d6037efe5e63d193ae8c586ff9d1e1326cae05cb Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 10 Sep 2023 23:07:00 -0600 Subject: [PATCH 24/69] service: mii: Move store data operations --- src/core/hle/service/mii/mii_manager.cpp | 70 +-- src/core/hle/service/mii/types/core_data.cpp | 4 + src/core/hle/service/mii/types/core_data.h | 1 + src/core/hle/service/mii/types/store_data.cpp | 439 +++++++++++++++++- src/core/hle/service/mii/types/store_data.h | 71 ++- 5 files changed, 512 insertions(+), 73 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 3483d95e54..dd7af531e0 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -31,76 +31,22 @@ std::array ResizeArray(const std::array& i } CharInfo ConvertStoreDataToInfo(const StoreData& data) { - const StoreDataBitFields& bf = data.core_data.data; - - return { - .create_id = data.create_id, - .name = data.core_data.name, - .font_region = static_cast(bf.font_region.Value()), - .favorite_color = static_cast(bf.favorite_color.Value()), - .gender = static_cast(bf.gender.Value()), - .height = static_cast(bf.height.Value()), - .build = static_cast(bf.build.Value()), - .type = static_cast(bf.type.Value()), - .region_move = static_cast(bf.region_move.Value()), - .faceline_type = static_cast(bf.faceline_type.Value()), - .faceline_color = static_cast(bf.faceline_color.Value()), - .faceline_wrinkle = static_cast(bf.faceline_wrinkle.Value()), - .faceline_make = static_cast(bf.faceline_makeup.Value()), - .hair_type = static_cast(bf.hair_type.Value()), - .hair_color = static_cast(bf.hair_color.Value()), - .hair_flip = static_cast(bf.hair_flip.Value()), - .eye_type = static_cast(bf.eye_type.Value()), - .eye_color = static_cast(bf.eye_color.Value()), - .eye_scale = static_cast(bf.eye_scale.Value()), - .eye_aspect = static_cast(bf.eye_aspect.Value()), - .eye_rotate = static_cast(bf.eye_rotate.Value()), - .eye_x = static_cast(bf.eye_x.Value()), - .eye_y = static_cast(bf.eye_y.Value()), - .eyebrow_type = static_cast(bf.eyebrow_type.Value()), - .eyebrow_color = static_cast(bf.eyebrow_color.Value()), - .eyebrow_scale = static_cast(bf.eyebrow_scale.Value()), - .eyebrow_aspect = static_cast(bf.eyebrow_aspect.Value()), - .eyebrow_rotate = static_cast(bf.eyebrow_rotate.Value()), - .eyebrow_x = static_cast(bf.eyebrow_x.Value()), - .eyebrow_y = static_cast(bf.eyebrow_y.Value() + 3), - .nose_type = static_cast(bf.nose_type.Value()), - .nose_scale = static_cast(bf.nose_scale.Value()), - .nose_y = static_cast(bf.nose_y.Value()), - .mouth_type = static_cast(bf.mouth_type.Value()), - .mouth_color = static_cast(bf.mouth_color.Value()), - .mouth_scale = static_cast(bf.mouth_scale.Value()), - .mouth_aspect = static_cast(bf.mouth_aspect.Value()), - .mouth_y = static_cast(bf.mouth_y.Value()), - .beard_color = static_cast(bf.beard_color.Value()), - .beard_type = static_cast(bf.beard_type.Value()), - .mustache_type = static_cast(bf.mustache_type.Value()), - .mustache_scale = static_cast(bf.mustache_scale.Value()), - .mustache_y = static_cast(bf.mustache_y.Value()), - .glasses_type = static_cast(bf.glasses_type.Value()), - .glasses_color = static_cast(bf.glasses_color.Value()), - .glasses_scale = static_cast(bf.glasses_scale.Value()), - .glasses_y = static_cast(bf.glasses_y.Value()), - .mole_type = static_cast(bf.mole_type.Value()), - .mole_scale = static_cast(bf.mole_scale.Value()), - .mole_x = static_cast(bf.mole_x.Value()), - .mole_y = static_cast(bf.mole_y.Value()), - .padding = 0, - }; + // Next Commit Will fix this one + return {}; } StoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { - CoreData core_data{}; - core_data.BuildRandom(age, gender, race); + StoreData store_data{}; + store_data.BuildRandom(age, gender, race); - return {DefaultMiiName, core_data.data, user_id}; + return store_data; } StoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { - CoreData core_data{}; - core_data.SetDefault(); + StoreData store_data{}; + store_data.BuildDefault(0); - return {DefaultMiiName, core_data.data, user_id}; + return store_data; } } // namespace diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp index 76c57fff2e..9d7e604a94 100644 --- a/src/core/hle/service/mii/types/core_data.cpp +++ b/src/core/hle/service/mii/types/core_data.cpp @@ -394,6 +394,10 @@ void CoreData::SetNickname(Nickname nickname) { name = nickname; } +u8 CoreData::GetFontRegion() const { + return static_cast(data.font_region.Value()); +} + u8 CoreData::GetFavoriteColor() const { return static_cast(data.favorite_color.Value()); } diff --git a/src/core/hle/service/mii/types/core_data.h b/src/core/hle/service/mii/types/core_data.h index 76daf4e6e8..411c123b3f 100644 --- a/src/core/hle/service/mii/types/core_data.h +++ b/src/core/hle/service/mii/types/core_data.h @@ -154,6 +154,7 @@ public: void SetMoleY(u8 value); void SetNickname(Nickname nickname); + u8 GetFontRegion() const; u8 GetFavoriteColor() const; u8 GetGender() const; u8 GetHeight() const; diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp index 459e8699fc..72c8fa2e95 100644 --- a/src/core/hle/service/mii/types/store_data.cpp +++ b/src/core/hle/service/mii/types/store_data.cpp @@ -2,19 +2,442 @@ // 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 { -StoreData::StoreData() = default; -StoreData::StoreData(const Nickname& name, const StoreDataBitFields& bit_fields, - const Common::UUID& user_id) { - core_data.name = name; - create_id = Common::UUID::MakeRandomRFC4122V4(); +void StoreData::BuildDefault(u32 mii_index) { + const auto& default_mii = RawData::DefaultMii[mii_index]; + core_data.SetDefault(); - core_data.data = bit_fields; - data_crc = MiiUtil::CalculateCrc16(&core_data.data, sizeof(core_data.data)); - device_crc = MiiUtil::CalculateCrc16(&user_id, sizeof(Common::UUID)); + core_data.SetFacelineType(static_cast(default_mii.face_type)); + core_data.SetFacelineColor( + RawData::GetFacelineColorFromVer3(static_cast(default_mii.face_color))); + core_data.SetFacelineWrinkle(static_cast(default_mii.face_wrinkle)); + core_data.SetFacelineMake(static_cast(default_mii.face_makeup)); + + core_data.SetHairType(static_cast(default_mii.hair_type)); + core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast(default_mii.hair_color))); + core_data.SetHairFlip(default_mii.hair_flip); + core_data.SetEyeType(static_cast(default_mii.eye_type)); + core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast(default_mii.eye_color))); + core_data.SetEyeScale(static_cast(default_mii.eye_scale)); + core_data.SetEyeAspect(static_cast(default_mii.eye_aspect)); + core_data.SetEyeRotate(static_cast(default_mii.eye_rotate)); + core_data.SetEyeX(static_cast(default_mii.eye_x)); + core_data.SetEyeY(static_cast(default_mii.eye_y)); + + core_data.SetEyebrowType(static_cast(default_mii.eyebrow_type)); + core_data.SetEyebrowColor( + RawData::GetHairColorFromVer3(static_cast(default_mii.eyebrow_color))); + core_data.SetEyebrowScale(static_cast(default_mii.eyebrow_scale)); + core_data.SetEyebrowAspect(static_cast(default_mii.eyebrow_aspect)); + core_data.SetEyebrowRotate(static_cast(default_mii.eyebrow_rotate)); + core_data.SetEyebrowX(static_cast(default_mii.eyebrow_x)); + core_data.SetEyebrowY(static_cast(default_mii.eyebrow_y)); + + core_data.SetNoseType(static_cast(default_mii.nose_type)); + core_data.SetNoseScale(static_cast(default_mii.nose_scale)); + core_data.SetNoseY(static_cast(default_mii.nose_y)); + + core_data.SetMouthType(static_cast(default_mii.mouth_type)); + core_data.SetMouthColor( + RawData::GetMouthColorFromVer3(static_cast(default_mii.mouth_color))); + core_data.SetMouthScale(static_cast(default_mii.mouth_scale)); + core_data.SetMouthAspect(static_cast(default_mii.mouth_aspect)); + core_data.SetMouthY(static_cast(default_mii.mouth_y)); + + core_data.SetMustacheType(default_mii.mustache_type); + core_data.SetBeardType(default_mii.beard_type); + core_data.SetBeardColor( + RawData::GetHairColorFromVer3(static_cast(default_mii.beard_color))); + core_data.SetMustacheScale(static_cast(default_mii.mustache_scale)); + core_data.SetMustacheY(static_cast(default_mii.mustache_y)); + + core_data.SetGlassType(static_cast(default_mii.glasses_type)); + core_data.SetGlassColor( + RawData::GetGlassColorFromVer3(static_cast(default_mii.glasses_color))); + core_data.SetGlassScale(static_cast(default_mii.glasses_scale)); + core_data.SetGlassY(static_cast(default_mii.glasses_y)); + + core_data.SetMoleType(static_cast(default_mii.mole_type)); + core_data.SetMoleScale(static_cast(default_mii.mole_scale)); + core_data.SetMoleX(static_cast(default_mii.mole_x)); + core_data.SetMoleY(static_cast(default_mii.mole_y)); + + core_data.SetHeight(static_cast(default_mii.height)); + core_data.SetBuild(static_cast(default_mii.weight)); + core_data.SetGender(default_mii.gender); + core_data.SetFavoriteColor(static_cast(default_mii.favorite_color)); + core_data.SetRegionMove(static_cast(default_mii.region_move)); + core_data.SetFontRegion(default_mii.font_region); + core_data.SetType(static_cast(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(default_mii.face_type)); + core_data.SetFacelineColor( + RawData::GetFacelineColorFromVer3(static_cast(default_mii.face_color))); + core_data.SetFacelineWrinkle(static_cast(default_mii.face_wrinkle)); + core_data.SetFacelineMake(static_cast(default_mii.face_makeup)); + + core_data.SetHairType(static_cast(default_mii.hair_type)); + core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast(default_mii.hair_color))); + core_data.SetHairFlip(default_mii.hair_flip); + core_data.SetEyeType(static_cast(default_mii.eye_type)); + core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast(default_mii.eye_color))); + core_data.SetEyeScale(static_cast(default_mii.eye_scale)); + core_data.SetEyeAspect(static_cast(default_mii.eye_aspect)); + core_data.SetEyeRotate(static_cast(default_mii.eye_rotate)); + core_data.SetEyeX(static_cast(default_mii.eye_x)); + core_data.SetEyeY(static_cast(default_mii.eye_y)); + + core_data.SetEyebrowType(static_cast(default_mii.eyebrow_type)); + core_data.SetEyebrowColor( + RawData::GetHairColorFromVer3(static_cast(default_mii.eyebrow_color))); + core_data.SetEyebrowScale(static_cast(default_mii.eyebrow_scale)); + core_data.SetEyebrowAspect(static_cast(default_mii.eyebrow_aspect)); + core_data.SetEyebrowRotate(static_cast(default_mii.eyebrow_rotate)); + core_data.SetEyebrowX(static_cast(default_mii.eyebrow_x)); + core_data.SetEyebrowY(static_cast(default_mii.eyebrow_y)); + + core_data.SetNoseType(static_cast(default_mii.nose_type)); + core_data.SetNoseScale(static_cast(default_mii.nose_scale)); + core_data.SetNoseY(static_cast(default_mii.nose_y)); + + core_data.SetMouthType(static_cast(default_mii.mouth_type)); + core_data.SetMouthColor( + RawData::GetMouthColorFromVer3(static_cast(default_mii.mouth_color))); + core_data.SetMouthScale(static_cast(default_mii.mouth_scale)); + core_data.SetMouthAspect(static_cast(default_mii.mouth_aspect)); + core_data.SetMouthY(static_cast(default_mii.mouth_y)); + + core_data.SetMustacheType(default_mii.mustache_type); + core_data.SetBeardType(default_mii.beard_type); + core_data.SetBeardColor( + RawData::GetHairColorFromVer3(static_cast(default_mii.beard_color))); + core_data.SetMustacheScale(static_cast(default_mii.mustache_scale)); + core_data.SetMustacheY(static_cast(default_mii.mustache_y)); + + core_data.SetGlassType(static_cast(default_mii.glasses_type)); + core_data.SetGlassColor( + RawData::GetGlassColorFromVer3(static_cast(default_mii.glasses_color))); + core_data.SetGlassScale(static_cast(default_mii.glasses_scale)); + core_data.SetGlassY(static_cast(default_mii.glasses_y)); + + core_data.SetMoleType(static_cast(default_mii.mole_type)); + core_data.SetMoleScale(static_cast(default_mii.mole_scale)); + core_data.SetMoleX(static_cast(default_mii.mole_x)); + core_data.SetMoleY(static_cast(default_mii.mole_y)); + + core_data.SetHeight(static_cast(default_mii.height)); + core_data.SetBuild(static_cast(default_mii.weight)); + core_data.SetGender(default_mii.gender); + core_data.SetFavoriteColor(static_cast(default_mii.favorite_color)); + core_data.SetRegionMove(static_cast(default_mii.region_move)); + core_data.SetFontRegion(default_mii.font_region); + core_data.SetType(static_cast(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; +} + +Common::UUID StoreData::GetCreateId() const { + return create_id; +} + +FontRegion StoreData::GetFontRegion() const { + return static_cast(core_data.GetFontRegion()); +} + +u8 StoreData::GetFavoriteColor() const { + return core_data.GetFavoriteColor(); +} + +u8 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(); +} + +u8 StoreData::GetFacelineType() const { + return core_data.GetFacelineType(); +} + +u8 StoreData::GetFacelineColor() const { + return core_data.GetFacelineColor(); +} + +u8 StoreData::GetFacelineWrinkle() const { + return core_data.GetFacelineWrinkle(); +} + +u8 StoreData::GetFacelineMake() const { + return core_data.GetFacelineMake(); +} + +u8 StoreData::GetHairType() const { + return core_data.GetHairType(); +} + +u8 StoreData::GetHairColor() const { + return core_data.GetHairColor(); +} + +u8 StoreData::GetHairFlip() const { + return core_data.GetHairFlip(); +} + +u8 StoreData::GetEyeType() const { + return core_data.GetEyeType(); +} + +u8 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(); +} + +u8 StoreData::GetEyebrowType() const { + return core_data.GetEyebrowType(); +} + +u8 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(); +} + +u8 StoreData::GetNoseType() const { + return core_data.GetNoseType(); +} + +u8 StoreData::GetNoseScale() const { + return core_data.GetNoseScale(); +} + +u8 StoreData::GetNoseY() const { + return core_data.GetNoseY(); +} + +u8 StoreData::GetMouthType() const { + return core_data.GetMouthType(); +} + +u8 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(); +} + +u8 StoreData::GetBeardColor() const { + return core_data.GetBeardColor(); +} + +u8 StoreData::GetBeardType() const { + return core_data.GetBeardType(); +} + +u8 StoreData::GetMustacheType() const { + return core_data.GetMustacheType(); +} + +u8 StoreData::GetMustacheScale() const { + return core_data.GetMustacheScale(); +} + +u8 StoreData::GetMustacheY() const { + return core_data.GetMustacheY(); +} + +u8 StoreData::GetGlassType() const { + return core_data.GetGlassType(); +} + +u8 StoreData::GetGlassColor() const { + return core_data.GetGlassColor(); +} + +u8 StoreData::GetGlassScale() const { + return core_data.GetGlassScale(); +} + +u8 StoreData::GetGlassY() const { + return core_data.GetGlassY(); +} + +u8 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.core_data.GetNickname(); + 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 index be69509672..cc8551a666 100644 --- a/src/core/hle/service/mii/types/store_data.h +++ b/src/core/hle/service/mii/types/store_data.h @@ -10,10 +10,75 @@ namespace Service::Mii { class StoreData { public: - StoreData(); - StoreData(const Nickname& name, const StoreDataBitFields& bit_fields, - const Common::UUID& user_id); + // 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); + + void SetInvalidName(); + + bool IsSpecial() const; + + u32 IsValid() const; + + Common::UUID GetCreateId() const; + FontRegion GetFontRegion() const; + u8 GetFavoriteColor() const; + u8 GetGender() const; + u8 GetHeight() const; + u8 GetBuild() const; + u8 GetType() const; + u8 GetRegionMove() const; + u8 GetFacelineType() const; + u8 GetFacelineColor() const; + u8 GetFacelineWrinkle() const; + u8 GetFacelineMake() const; + u8 GetHairType() const; + u8 GetHairColor() const; + u8 GetHairFlip() const; + u8 GetEyeType() const; + u8 GetEyeColor() const; + u8 GetEyeScale() const; + u8 GetEyeAspect() const; + u8 GetEyeRotate() const; + u8 GetEyeX() const; + u8 GetEyeY() const; + u8 GetEyebrowType() const; + u8 GetEyebrowColor() const; + u8 GetEyebrowScale() const; + u8 GetEyebrowAspect() const; + u8 GetEyebrowRotate() const; + u8 GetEyebrowX() const; + u8 GetEyebrowY() const; + u8 GetNoseType() const; + u8 GetNoseScale() const; + u8 GetNoseY() const; + u8 GetMouthType() const; + u8 GetMouthColor() const; + u8 GetMouthScale() const; + u8 GetMouthAspect() const; + u8 GetMouthY() const; + u8 GetBeardColor() const; + u8 GetBeardType() const; + u8 GetMustacheType() const; + u8 GetMustacheScale() const; + u8 GetMustacheY() const; + u8 GetGlassType() const; + u8 GetGlassColor() const; + u8 GetGlassScale() const; + u8 GetGlassY() const; + u8 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{}; From 36290f9a0ac953ce57a663b5ba817d7e3bb5a33c Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 10 Sep 2023 23:17:50 -0600 Subject: [PATCH 25/69] service: mii: move char info operations --- src/core/hle/service/mii/mii_manager.cpp | 30 +- src/core/hle/service/mii/mii_types.h | 29 +- src/core/hle/service/mii/types/char_info.cpp | 478 ++++++++++++++++++- src/core/hle/service/mii/types/char_info.h | 60 +++ 4 files changed, 576 insertions(+), 21 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index dd7af531e0..2137a9af19 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -21,18 +21,10 @@ namespace { constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; -constexpr Nickname DefaultMiiName{u'n', u'o', u' ', u'n', u'a', u'm', u'e'}; - -template -std::array ResizeArray(const std::array& in) { - std::array out{}; - std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize)); - return out; -} - CharInfo ConvertStoreDataToInfo(const StoreData& data) { - // Next Commit Will fix this one - return {}; + CharInfo char_info{}; + char_info.SetFromStoreData(data); + return char_info; } StoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { @@ -112,14 +104,14 @@ CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const { return { - .faceline_color = static_cast(mii.faceline_color & 0xf), - .hair_color = static_cast(mii.hair_color & 0x7f), - .eye_color = static_cast(mii.eyebrow_color & 0x7f), - .eyebrow_color = static_cast(mii.eyebrow_color & 0x7f), - .mouth_color = static_cast(mii.mouth_color & 0x7f), - .beard_color = static_cast(mii.beard_color & 0x7f), - .glass_color = static_cast(mii.glasses_color & 0x7f), - .glass_type = static_cast(mii.glasses_type & 0x1f), + .faceline_color = static_cast(mii.GetFacelineColor() & 0xf), + .hair_color = static_cast(mii.GetHairColor() & 0x7f), + .eye_color = static_cast(mii.GetEyeColor() & 0x7f), + .eyebrow_color = static_cast(mii.GetEyebrowColor() & 0x7f), + .mouth_color = static_cast(mii.GetMouthColor() & 0x7f), + .beard_color = static_cast(mii.GetBeardColor() & 0x7f), + .glass_color = static_cast(mii.GetGlassColor() & 0x7f), + .glass_type = static_cast(mii.GetGlassType() & 0x1f), }; } diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h index ff836dcf24..20d4e40ab4 100644 --- a/src/core/hle/service/mii/mii_types.h +++ b/src/core/hle/service/mii/mii_types.h @@ -86,7 +86,34 @@ enum class SourceFlag : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); -using Nickname = std::array; +struct Nickname { + static constexpr std::size_t MaxNameSize = 10; + std::array 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; + } + + bool operator==(const Nickname& name) { + return data == name.data; + }; +}; +static_assert(sizeof(Nickname) == 0x14, "Nickname is an invalid size"); struct NfpStoreDataExtension { u8 faceline_color; diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp index 250fad44bc..e1d8748606 100644 --- a/src/core/hle/service/mii/types/char_info.cpp +++ b/src/core/hle/service/mii/types/char_info.cpp @@ -2,5 +2,481 @@ // 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 {} // namespace Service::Mii +namespace Service::Mii { + +void CharInfo::SetFromStoreData(const StoreData& store_data) { + name = store_data.GetNickname(); + null_terminator = '\0'; + create_id = store_data.GetCreateId(); + font_region = static_cast(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(); + glasses_type = store_data.GetGlassType(); + glasses_color = store_data.GetGlassColor(); + glasses_scale = store_data.GetGlassScale(); + glasses_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'; +} + +u32 CharInfo::Verify() const { + if (!create_id.IsValid()) { + return 0x32; + } + if (!name.IsValid()) { + return 0x33; + } + if (3 < font_region) { + return 0x17; + } + if (0xb < favorite_color) { + return 0x16; + } + if (1 < gender) { + return 0x18; + } + if (height < 0) { + return 0x20; + } + if (build < 0) { + return 3; + } + if (1 < type) { + return 0x35; + } + if (3 < region_move) { + return 0x31; + } + if (0xb < faceline_type) { + return 0x15; + } + if (9 < faceline_color) { + return 0x12; + } + if (0xb < faceline_wrinkle) { + return 0x14; + } + if (0xb < faceline_make) { + return 0x13; + } + if (0x83 < hair_type) { + return 0x1f; + } + if (99 < hair_color) { + return 0x1d; + } + if (1 < hair_flip) { + return 0x1e; + } + if (0x3b < eye_type) { + return 8; + } + if (99 < eye_color) { + return 5; + } + if (7 < eye_scale) { + return 7; + } + if (6 < eye_aspect) { + return 4; + } + if (7 < eye_rotate) { + return 6; + } + if (0xc < eye_x) { + return 9; + } + if (0x12 < eye_y) { + return 10; + } + if (0x17 < eyebrow_type) { + return 0xf; + } + if (99 < eyebrow_color) { + return 0xc; + } + if (8 < eyebrow_scale) { + return 0xe; + } + if (6 < eyebrow_aspect) { + return 0xb; + } + if (0xb < eyebrow_rotate) { + return 0xd; + } + if (0xc < eyebrow_x) { + return 0x10; + } + if (0xf < eyebrow_y - 3) { + return 0x11; + } + if (0x11 < nose_type) { + return 0x2f; + } + if (nose_scale >= 9) { + return 0x2e; + } + if (0x12 < nose_y) { + return 0x30; + } + if (0x23 < mouth_type) { + return 0x28; + } + if (99 < mouth_color) { + return 0x26; + } + if (8 < mouth_scale) { + return 0x27; + } + if (6 < mouth_aspect) { + return 0x25; + } + if (0x12 < mouth_y) { + return 0x29; + } + if (99 < beard_color) { + return 1; + } + if (5 < beard_type) { + return 2; + } + if (5 < mustache_type) { + return 0x2b; + } + if (8 < mustache_scale) { + return 0x2a; + } + if (0x10 < mustache_y) { + return 0x2c; + } + if (0x13 < glasses_type) { + return 0x1b; + } + if (99 < glasses_color) { + return 0x19; + } + if (7 < glasses_scale) { + return 0x1a; + } + if (0x14 < glasses_y) { + return 0x1c; + } + if (mole_type >= 2) { + return 0x22; + } + if (8 < mole_scale) { + return 0x21; + } + if (mole_x >= 0x11) { + return 0x23; + } + if (0x1e < mole_y) { + return 0x24; + } + return 0; +} + +Common::UUID CharInfo::GetCreateId() const { + return create_id; +} + +Nickname CharInfo::GetNickname() const { + return name; +} + +u8 CharInfo::GetFontRegion() const { + return font_region; +} + +u8 CharInfo::GetFavoriteColor() const { + return favorite_color; +} + +u8 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; +} + +u8 CharInfo::GetFacelineType() const { + return faceline_type; +} + +u8 CharInfo::GetFacelineColor() const { + return faceline_color; +} + +u8 CharInfo::GetFacelineWrinkle() const { + return faceline_wrinkle; +} + +u8 CharInfo::GetFacelineMake() const { + return faceline_make; +} + +u8 CharInfo::GetHairType() const { + return hair_type; +} + +u8 CharInfo::GetHairColor() const { + return hair_color; +} + +u8 CharInfo::GetHairFlip() const { + return hair_flip; +} + +u8 CharInfo::GetEyeType() const { + return eye_type; +} + +u8 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; +} + +u8 CharInfo::GetEyebrowType() const { + return eyebrow_type; +} + +u8 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; +} + +u8 CharInfo::GetNoseType() const { + return nose_type; +} + +u8 CharInfo::GetNoseScale() const { + return nose_scale; +} + +u8 CharInfo::GetNoseY() const { + return nose_y; +} + +u8 CharInfo::GetMouthType() const { + return mouth_type; +} + +u8 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; +} + +u8 CharInfo::GetBeardColor() const { + return beard_color; +} + +u8 CharInfo::GetBeardType() const { + return beard_type; +} + +u8 CharInfo::GetMustacheType() const { + return mustache_type; +} + +u8 CharInfo::GetMustacheScale() const { + return mustache_scale; +} + +u8 CharInfo::GetMustacheY() const { + return mustache_y; +} + +u8 CharInfo::GetGlassType() const { + return glasses_type; +} + +u8 CharInfo::GetGlassColor() const { + return glasses_color; +} + +u8 CharInfo::GetGlassScale() const { + return glasses_scale; +} + +u8 CharInfo::GetGlassY() const { + return glasses_y; +} + +u8 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() == 0; + is_identical &= name == info.GetNickname(); + 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 &= glasses_type == info.GetGlassType(); + is_identical &= glasses_color == info.GetGlassColor(); + is_identical &= glasses_scale == info.GetGlassScale(); + is_identical &= glasses_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 index cdebb1c9de..4f70edc248 100644 --- a/src/core/hle/service/mii/types/char_info.h +++ b/src/core/hle/service/mii/types/char_info.h @@ -6,10 +6,70 @@ #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); + + u32 Verify() const; + + Common::UUID GetCreateId() const; + Nickname GetNickname() const; + u8 GetFontRegion() const; + u8 GetFavoriteColor() const; + u8 GetGender() const; + u8 GetHeight() const; + u8 GetBuild() const; + u8 GetType() const; + u8 GetRegionMove() const; + u8 GetFacelineType() const; + u8 GetFacelineColor() const; + u8 GetFacelineWrinkle() const; + u8 GetFacelineMake() const; + u8 GetHairType() const; + u8 GetHairColor() const; + u8 GetHairFlip() const; + u8 GetEyeType() const; + u8 GetEyeColor() const; + u8 GetEyeScale() const; + u8 GetEyeAspect() const; + u8 GetEyeRotate() const; + u8 GetEyeX() const; + u8 GetEyeY() const; + u8 GetEyebrowType() const; + u8 GetEyebrowColor() const; + u8 GetEyebrowScale() const; + u8 GetEyebrowAspect() const; + u8 GetEyebrowRotate() const; + u8 GetEyebrowX() const; + u8 GetEyebrowY() const; + u8 GetNoseType() const; + u8 GetNoseScale() const; + u8 GetNoseY() const; + u8 GetMouthType() const; + u8 GetMouthColor() const; + u8 GetMouthScale() const; + u8 GetMouthAspect() const; + u8 GetMouthY() const; + u8 GetBeardColor() const; + u8 GetBeardType() const; + u8 GetMustacheType() const; + u8 GetMustacheScale() const; + u8 GetMustacheY() const; + u8 GetGlassType() const; + u8 GetGlassColor() const; + u8 GetGlassScale() const; + u8 GetGlassY() const; + u8 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; From 571399930cc3578acff064a7087fe85e7b2dd9b7 Mon Sep 17 00:00:00 2001 From: german77 Date: Mon, 11 Sep 2023 00:23:46 -0600 Subject: [PATCH 26/69] service: mii: Fix ver3 inconsistencies --- src/core/hle/service/mii/mii_manager.cpp | 17 +- src/core/hle/service/mii/mii_manager.h | 3 - src/core/hle/service/mii/mii_types.h | 16 - src/core/hle/service/mii/types/char_info.cpp | 2 +- src/core/hle/service/mii/types/store_data.cpp | 202 ++++++++++++- src/core/hle/service/mii/types/store_data.h | 54 +++- .../hle/service/mii/types/ver3_store_data.cpp | 280 ++++++++++-------- .../hle/service/mii/types/ver3_store_data.h | 80 ++--- src/core/hle/service/nfc/common/device.cpp | 16 +- 9 files changed, 463 insertions(+), 207 deletions(-) diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 2137a9af19..3951e0b9c8 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -98,23 +98,12 @@ CharInfo MiiManager::BuildDefault(std::size_t index) { CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { CharInfo char_info{}; - mii_v3.BuildToStoreData(char_info); + StoreData store_data{}; + mii_v3.BuildToStoreData(store_data); + char_info.SetFromStoreData(store_data); return char_info; } -NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const { - return { - .faceline_color = static_cast(mii.GetFacelineColor() & 0xf), - .hair_color = static_cast(mii.GetHairColor() & 0x7f), - .eye_color = static_cast(mii.GetEyeColor() & 0x7f), - .eyebrow_color = static_cast(mii.GetEyebrowColor() & 0x7f), - .mouth_color = static_cast(mii.GetMouthColor() & 0x7f), - .beard_color = static_cast(mii.GetBeardColor() & 0x7f), - .glass_color = static_cast(mii.GetGlassColor() & 0x7f), - .glass_type = static_cast(mii.GetGlassType() & 0x1f), - }; -} - std::vector MiiManager::GetDefault(SourceFlag source_flag) { std::vector result; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 0a47e613fb..6098474e97 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -30,9 +30,6 @@ public: std::vector GetDefault(SourceFlag source_flag); Result GetIndex(const CharInfo& info, u32& index); - // This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData - NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const; - struct MiiDatabase { u32 magic{}; // 'NFDB' std::array miis{}; diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h index 20d4e40ab4..d62005f614 100644 --- a/src/core/hle/service/mii/mii_types.h +++ b/src/core/hle/service/mii/mii_types.h @@ -108,25 +108,9 @@ struct Nickname { } return index == MaxNameSize; } - - bool operator==(const Nickname& name) { - return data == name.data; - }; }; static_assert(sizeof(Nickname) == 0x14, "Nickname 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"); - struct DefaultMii { u32 face_type{}; u32 face_color{}; diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp index e1d8748606..0f9c37b847 100644 --- a/src/core/hle/service/mii/types/char_info.cpp +++ b/src/core/hle/service/mii/types/char_info.cpp @@ -425,7 +425,7 @@ u8 CharInfo::GetMoleY() const { bool CharInfo::operator==(const CharInfo& info) { bool is_identical = info.Verify() == 0; - is_identical &= name == info.GetNickname(); + 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(); diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp index 72c8fa2e95..91dfd3271c 100644 --- a/src/core/hle/service/mii/types/store_data.cpp +++ b/src/core/hle/service/mii/types/store_data.cpp @@ -180,6 +180,206 @@ u32 StoreData::IsValid() const { return 0; } +void StoreData::SetFontRegion(FontRegion value) { + core_data.SetFontRegion(value); +} + +void StoreData::SetFavoriteColor(u8 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(u8 value) { + core_data.SetFacelineType(value); +} + +void StoreData::SetFacelineColor(u8 value) { + core_data.SetFacelineColor(value); +} + +void StoreData::SetFacelineWrinkle(u8 value) { + core_data.SetFacelineWrinkle(value); +} + +void StoreData::SetFacelineMake(u8 value) { + core_data.SetFacelineMake(value); +} + +void StoreData::SetHairType(u8 value) { + core_data.SetHairType(value); +} + +void StoreData::SetHairColor(u8 value) { + core_data.SetHairColor(value); +} + +void StoreData::SetHairFlip(HairFlip value) { + core_data.SetHairFlip(value); +} + +void StoreData::SetEyeType(u8 value) { + core_data.SetEyeType(value); +} + +void StoreData::SetEyeColor(u8 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(u8 value) { + core_data.SetEyebrowType(value); +} + +void StoreData::SetEyebrowColor(u8 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(u8 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(u8 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(u8 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(u8 value) { + core_data.SetGlassType(value); +} + +void StoreData::SetGlassColor(u8 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(u8 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; } @@ -386,7 +586,7 @@ Nickname StoreData::GetNickname() const { bool StoreData::operator==(const StoreData& data) { bool is_identical = data.core_data.IsValid() == 0; - is_identical &= core_data.GetNickname() == data.core_data.GetNickname(); + 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(); diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h index cc8551a666..1e010000b5 100644 --- a/src/core/hle/service/mii/types/store_data.h +++ b/src/core/hle/service/mii/types/store_data.h @@ -18,12 +18,62 @@ public: // nn::mii::detail::StoreDataRaw::BuildRandom void BuildRandom(Age age, Gender gender, Race race); - void SetInvalidName(); - bool IsSpecial() const; u32 IsValid() const; + void SetFontRegion(FontRegion value); + void SetFavoriteColor(u8 value); + void SetGender(Gender value); + void SetHeight(u8 value); + void SetBuild(u8 value); + void SetType(u8 value); + void SetRegionMove(u8 value); + void SetFacelineType(u8 value); + void SetFacelineColor(u8 value); + void SetFacelineWrinkle(u8 value); + void SetFacelineMake(u8 value); + void SetHairType(u8 value); + void SetHairColor(u8 value); + void SetHairFlip(HairFlip value); + void SetEyeType(u8 value); + void SetEyeColor(u8 value); + void SetEyeScale(u8 value); + void SetEyeAspect(u8 value); + void SetEyeRotate(u8 value); + void SetEyeX(u8 value); + void SetEyeY(u8 value); + void SetEyebrowType(u8 value); + void SetEyebrowColor(u8 value); + void SetEyebrowScale(u8 value); + void SetEyebrowAspect(u8 value); + void SetEyebrowRotate(u8 value); + void SetEyebrowX(u8 value); + void SetEyebrowY(u8 value); + void SetNoseType(u8 value); + void SetNoseScale(u8 value); + void SetNoseY(u8 value); + void SetMouthType(u8 value); + void SetMouthColor(u8 value); + void SetMouthScale(u8 value); + void SetMouthAspect(u8 value); + void SetMouthY(u8 value); + void SetBeardColor(u8 value); + void SetBeardType(BeardType value); + void SetMustacheType(MustacheType value); + void SetMustacheScale(u8 value); + void SetMustacheY(u8 value); + void SetGlassType(u8 value); + void SetGlassColor(u8 value); + void SetGlassScale(u8 value); + void SetGlassY(u8 value); + void SetMoleType(u8 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; u8 GetFavoriteColor() const; diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index c774f4b47a..53a3fe44bc 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -2,160 +2,180 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/mii/mii_util.h" -#include "core/hle/service/mii/types/char_info.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 Ver3StoreData::BuildToStoreData(CharInfo& out_char_info) const { +void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) { + faceline_color = static_cast(store_data.GetFacelineColor() & 0xf); + hair_color = static_cast(store_data.GetHairColor() & 0x7f); + eye_color = static_cast(store_data.GetEyeColor() & 0x7f); + eyebrow_color = static_cast(store_data.GetEyebrowColor() & 0x7f); + mouth_color = static_cast(store_data.GetMouthColor() & 0x7f); + beard_color = static_cast(store_data.GetBeardColor() & 0x7f); + glass_color = static_cast(store_data.GetGlassColor() & 0x7f); + glass_type = static_cast(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_char_info.gender = static_cast(mii_information.gender); - out_char_info.favorite_color = static_cast(mii_information.favorite_color); - out_char_info.height = height; - out_char_info.build = build; + out_store_data.SetGender(static_cast(static_cast(mii_information.gender))); + out_store_data.SetFavoriteColor(static_cast(mii_information.favorite_color)); + out_store_data.SetHeight(height); + out_store_data.SetBuild(build); // Copy name until string terminator - out_char_info.name = {}; - for (std::size_t index = 0; index < out_char_info.name.size() - 1; index++) { - out_char_info.name[index] = mii_name[index]; - if (out_char_info.name[index] == 0) { + Nickname name = {}; + for (std::size_t index = 0; index < name.data.size() - 1; index++) { + name.data[index] = mii_name[index]; + if (name.data[index] == 0) { break; } } - out_char_info.font_region = region_information.character_set; + out_store_data.SetNickname(name); + out_store_data.SetFontRegion( + static_cast(static_cast(region_information.font_region))); - out_char_info.faceline_type = appearance_bits1.face_shape; - out_char_info.faceline_color = appearance_bits1.skin_color; - out_char_info.faceline_wrinkle = appearance_bits2.wrinkles; - out_char_info.faceline_make = appearance_bits2.makeup; + out_store_data.SetFacelineType(appearance_bits1.faceline_type); + out_store_data.SetFacelineColor(appearance_bits1.faceline_color); + out_store_data.SetFacelineWrinkle(appearance_bits2.faceline_wrinkle); + out_store_data.SetFacelineMake(appearance_bits2.faceline_make); - out_char_info.hair_type = hair_style; - out_char_info.hair_color = appearance_bits3.hair_color; - out_char_info.hair_flip = appearance_bits3.flip_hair; + out_store_data.SetHairType(hair_type); + out_store_data.SetHairColor(appearance_bits3.hair_color); + out_store_data.SetHairFlip(static_cast(static_cast(appearance_bits3.hair_flip))); - out_char_info.eye_type = static_cast(appearance_bits4.eye_type); - out_char_info.eye_color = static_cast(appearance_bits4.eye_color); - out_char_info.eye_scale = static_cast(appearance_bits4.eye_scale); - out_char_info.eye_aspect = static_cast(appearance_bits4.eye_vertical_stretch); - out_char_info.eye_rotate = static_cast(appearance_bits4.eye_rotation); - out_char_info.eye_x = static_cast(appearance_bits4.eye_spacing); - out_char_info.eye_y = static_cast(appearance_bits4.eye_y_position); + out_store_data.SetEyeType(static_cast(appearance_bits4.eye_type)); + out_store_data.SetEyeColor(static_cast(appearance_bits4.eye_color)); + out_store_data.SetEyeScale(static_cast(appearance_bits4.eye_scale)); + out_store_data.SetEyeAspect(static_cast(appearance_bits4.eye_aspect)); + out_store_data.SetEyeRotate(static_cast(appearance_bits4.eye_rotate)); + out_store_data.SetEyeX(static_cast(appearance_bits4.eye_x)); + out_store_data.SetEyeY(static_cast(appearance_bits4.eye_y)); - out_char_info.eyebrow_type = static_cast(appearance_bits5.eyebrow_style); - out_char_info.eyebrow_color = static_cast(appearance_bits5.eyebrow_color); - out_char_info.eyebrow_scale = static_cast(appearance_bits5.eyebrow_scale); - out_char_info.eyebrow_aspect = static_cast(appearance_bits5.eyebrow_yscale); - out_char_info.eyebrow_rotate = static_cast(appearance_bits5.eyebrow_rotation); - out_char_info.eyebrow_x = static_cast(appearance_bits5.eyebrow_spacing); - out_char_info.eyebrow_y = static_cast(appearance_bits5.eyebrow_y_position); + out_store_data.SetEyebrowType(static_cast(appearance_bits5.eyebrow_type)); + out_store_data.SetEyebrowColor(static_cast(appearance_bits5.eyebrow_color)); + out_store_data.SetEyebrowScale(static_cast(appearance_bits5.eyebrow_scale)); + out_store_data.SetEyebrowAspect(static_cast(appearance_bits5.eyebrow_aspect)); + out_store_data.SetEyebrowRotate(static_cast(appearance_bits5.eyebrow_rotate)); + out_store_data.SetEyebrowX(static_cast(appearance_bits5.eyebrow_x)); + out_store_data.SetEyebrowY(static_cast(appearance_bits5.eyebrow_y)); - out_char_info.nose_type = static_cast(appearance_bits6.nose_type); - out_char_info.nose_scale = static_cast(appearance_bits6.nose_scale); - out_char_info.nose_y = static_cast(appearance_bits6.nose_y_position); + out_store_data.SetNoseType(static_cast(appearance_bits6.nose_type)); + out_store_data.SetNoseScale(static_cast(appearance_bits6.nose_scale)); + out_store_data.SetNoseY(static_cast(appearance_bits6.nose_y)); - out_char_info.mouth_type = static_cast(appearance_bits7.mouth_type); - out_char_info.mouth_color = static_cast(appearance_bits7.mouth_color); - out_char_info.mouth_scale = static_cast(appearance_bits7.mouth_scale); - out_char_info.mouth_aspect = static_cast(appearance_bits7.mouth_horizontal_stretch); - out_char_info.mouth_y = static_cast(appearance_bits8.mouth_y_position); + out_store_data.SetMouthType(static_cast(appearance_bits7.mouth_type)); + out_store_data.SetMouthColor(static_cast(appearance_bits7.mouth_color)); + out_store_data.SetMouthScale(static_cast(appearance_bits7.mouth_scale)); + out_store_data.SetMouthAspect(static_cast(appearance_bits7.mouth_aspect)); + out_store_data.SetMouthY(static_cast(appearance_bits8.mouth_y)); - out_char_info.mustache_type = static_cast(appearance_bits8.mustache_type); - out_char_info.mustache_scale = static_cast(appearance_bits9.mustache_scale); - out_char_info.mustache_y = static_cast(appearance_bits9.mustache_y_position); + out_store_data.SetMustacheType( + static_cast(static_cast(appearance_bits8.mustache_type))); + out_store_data.SetMustacheScale(static_cast(appearance_bits9.mustache_scale)); + out_store_data.SetMustacheY(static_cast(appearance_bits9.mustache_y)); - out_char_info.beard_type = static_cast(appearance_bits9.bear_type); - out_char_info.beard_color = static_cast(appearance_bits9.facial_hair_color); + out_store_data.SetBeardType( + static_cast(static_cast(appearance_bits9.beard_type))); + out_store_data.SetBeardColor(static_cast(appearance_bits9.beard_color)); - out_char_info.glasses_type = static_cast(appearance_bits10.glasses_type); - out_char_info.glasses_color = static_cast(appearance_bits10.glasses_color); - out_char_info.glasses_scale = static_cast(appearance_bits10.glasses_scale); - out_char_info.glasses_y = static_cast(appearance_bits10.glasses_y_position); + out_store_data.SetGlassType(static_cast(appearance_bits10.glass_type)); + out_store_data.SetGlassColor(static_cast(appearance_bits10.glass_color)); + out_store_data.SetGlassScale(static_cast(appearance_bits10.glass_scale)); + out_store_data.SetGlassY(static_cast(appearance_bits10.glass_y)); - out_char_info.mole_type = static_cast(appearance_bits11.mole_enabled); - out_char_info.mole_scale = static_cast(appearance_bits11.mole_scale); - out_char_info.mole_x = static_cast(appearance_bits11.mole_x_position); - out_char_info.mole_y = static_cast(appearance_bits11.mole_y_position); + out_store_data.SetMoleType(static_cast(appearance_bits11.mole_type)); + out_store_data.SetMoleScale(static_cast(appearance_bits11.mole_scale)); + out_store_data.SetMoleX(static_cast(appearance_bits11.mole_x)); + out_store_data.SetMoleY(static_cast(appearance_bits11.mole_y)); } -void Ver3StoreData::BuildFromStoreData(const CharInfo& char_info) { +void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { version = 1; - mii_information.gender.Assign(char_info.gender); - mii_information.favorite_color.Assign(char_info.favorite_color); - height = char_info.height; - build = char_info.build; + mii_information.gender.Assign(store_data.GetGender()); + mii_information.favorite_color.Assign(store_data.GetFavoriteColor()); + height = store_data.GetHeight(); + build = store_data.GetBuild(); // Copy name until string terminator mii_name = {}; - for (std::size_t index = 0; index < char_info.name.size() - 1; index++) { - mii_name[index] = char_info.name[index]; + for (std::size_t index = 0; index < store_data.GetNickname().data.size() - 1; index++) { + mii_name[index] = store_data.GetNickname().data[index]; if (mii_name[index] == 0) { break; } } - region_information.character_set.Assign(char_info.font_region); + region_information.font_region.Assign(static_cast(store_data.GetFontRegion())); - appearance_bits1.face_shape.Assign(char_info.faceline_type); - appearance_bits2.wrinkles.Assign(char_info.faceline_wrinkle); - appearance_bits2.makeup.Assign(char_info.faceline_make); + appearance_bits1.faceline_type.Assign(store_data.GetFacelineType()); + appearance_bits2.faceline_wrinkle.Assign(store_data.GetFacelineWrinkle()); + appearance_bits2.faceline_make.Assign(store_data.GetFacelineMake()); - hair_style = char_info.hair_type; - appearance_bits3.flip_hair.Assign(char_info.hair_flip); + hair_type = store_data.GetHairType(); + appearance_bits3.hair_flip.Assign(store_data.GetHairFlip()); - appearance_bits4.eye_type.Assign(char_info.eye_type); - appearance_bits4.eye_scale.Assign(char_info.eye_scale); - appearance_bits4.eye_vertical_stretch.Assign(char_info.eye_aspect); - appearance_bits4.eye_rotation.Assign(char_info.eye_rotate); - appearance_bits4.eye_spacing.Assign(char_info.eye_x); - appearance_bits4.eye_y_position.Assign(char_info.eye_y); + appearance_bits4.eye_type.Assign(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_style.Assign(char_info.eyebrow_type); - appearance_bits5.eyebrow_scale.Assign(char_info.eyebrow_scale); - appearance_bits5.eyebrow_yscale.Assign(char_info.eyebrow_aspect); - appearance_bits5.eyebrow_rotation.Assign(char_info.eyebrow_rotate); - appearance_bits5.eyebrow_spacing.Assign(char_info.eyebrow_x); - appearance_bits5.eyebrow_y_position.Assign(char_info.eyebrow_y); + appearance_bits5.eyebrow_type.Assign(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(char_info.nose_type); - appearance_bits6.nose_scale.Assign(char_info.nose_scale); - appearance_bits6.nose_y_position.Assign(char_info.nose_y); + appearance_bits6.nose_type.Assign(store_data.GetNoseType()); + appearance_bits6.nose_scale.Assign(store_data.GetNoseScale()); + appearance_bits6.nose_y.Assign(store_data.GetNoseY()); - appearance_bits7.mouth_type.Assign(char_info.mouth_type); - appearance_bits7.mouth_scale.Assign(char_info.mouth_scale); - appearance_bits7.mouth_horizontal_stretch.Assign(char_info.mouth_aspect); - appearance_bits8.mouth_y_position.Assign(char_info.mouth_y); + appearance_bits7.mouth_type.Assign(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(char_info.mustache_type); - appearance_bits9.mustache_scale.Assign(char_info.mustache_scale); - appearance_bits9.mustache_y_position.Assign(char_info.mustache_y); + appearance_bits8.mustache_type.Assign(store_data.GetMustacheType()); + appearance_bits9.mustache_scale.Assign(store_data.GetMustacheScale()); + appearance_bits9.mustache_y.Assign(store_data.GetMustacheY()); - appearance_bits9.bear_type.Assign(char_info.beard_type); + appearance_bits9.beard_type.Assign(store_data.GetBeardType()); - appearance_bits10.glasses_scale.Assign(char_info.glasses_scale); - appearance_bits10.glasses_y_position.Assign(char_info.glasses_y); + appearance_bits10.glass_scale.Assign(store_data.GetGlassScale()); + appearance_bits10.glass_y.Assign(store_data.GetGlassY()); - appearance_bits11.mole_enabled.Assign(char_info.mole_type); - appearance_bits11.mole_scale.Assign(char_info.mole_scale); - appearance_bits11.mole_x_position.Assign(char_info.mole_x); - appearance_bits11.mole_y_position.Assign(char_info.mole_y); + appearance_bits11.mole_type.Assign(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.skin_color.Assign(RawData::FromVer3GetFacelineColor(char_info.faceline_color)); - appearance_bits3.hair_color.Assign(RawData::FromVer3GetHairColor(char_info.hair_color)); - appearance_bits4.eye_color.Assign(RawData::FromVer3GetEyeColor(char_info.eye_color)); - appearance_bits5.eyebrow_color.Assign(RawData::FromVer3GetHairColor(char_info.eyebrow_color)); - appearance_bits7.mouth_color.Assign(RawData::FromVer3GetMouthlineColor(char_info.mouth_color)); - appearance_bits9.facial_hair_color.Assign(RawData::FromVer3GetHairColor(char_info.beard_color)); - appearance_bits10.glasses_color.Assign(RawData::FromVer3GetGlassColor(char_info.glasses_color)); - appearance_bits10.glasses_type.Assign(RawData::FromVer3GetGlassType(char_info.glasses_type)); + appearance_bits1.faceline_color.Assign( + RawData::FromVer3GetFacelineColor(store_data.GetFacelineColor())); + appearance_bits3.hair_color.Assign(RawData::FromVer3GetHairColor(store_data.GetHairColor())); + appearance_bits4.eye_color.Assign(RawData::FromVer3GetEyeColor(store_data.GetEyeColor())); + appearance_bits5.eyebrow_color.Assign( + RawData::FromVer3GetHairColor(store_data.GetEyebrowColor())); + appearance_bits7.mouth_color.Assign( + RawData::FromVer3GetMouthlineColor(store_data.GetMouthColor())); + appearance_bits9.beard_color.Assign(RawData::FromVer3GetHairColor(store_data.GetBeardColor())); + appearance_bits10.glass_color.Assign( + RawData::FromVer3GetGlassColor(store_data.GetGlassColor())); + appearance_bits10.glass_type.Assign(RawData::FromVer3GetGlassType(store_data.GetGlassType())); crc = MiiUtil::CalculateCrc16(&version, sizeof(Ver3StoreData) - sizeof(u16)); } @@ -171,56 +191,56 @@ u32 Ver3StoreData::IsValid() const { is_valid = is_valid && (height < 128); is_valid = is_valid && (build < 128); - is_valid = is_valid && (appearance_bits1.face_shape < 12); - is_valid = is_valid && (appearance_bits1.skin_color < 7); - is_valid = is_valid && (appearance_bits2.wrinkles < 12); - is_valid = is_valid && (appearance_bits2.makeup < 12); + is_valid = is_valid && (appearance_bits1.faceline_type < 12); + is_valid = is_valid && (appearance_bits1.faceline_color < 7); + is_valid = is_valid && (appearance_bits2.faceline_wrinkle < 12); + is_valid = is_valid && (appearance_bits2.faceline_make < 12); - is_valid = is_valid && (hair_style < 132); + is_valid = is_valid && (hair_type < 132); is_valid = is_valid && (appearance_bits3.hair_color < 8); is_valid = is_valid && (appearance_bits4.eye_type < 60); is_valid = is_valid && (appearance_bits4.eye_color < 6); is_valid = is_valid && (appearance_bits4.eye_scale < 8); - is_valid = is_valid && (appearance_bits4.eye_vertical_stretch < 7); - is_valid = is_valid && (appearance_bits4.eye_rotation < 8); - is_valid = is_valid && (appearance_bits4.eye_spacing < 13); - is_valid = is_valid && (appearance_bits4.eye_y_position < 19); + is_valid = is_valid && (appearance_bits4.eye_aspect < 7); + is_valid = is_valid && (appearance_bits4.eye_rotate < 8); + is_valid = is_valid && (appearance_bits4.eye_x < 13); + is_valid = is_valid && (appearance_bits4.eye_y < 19); - is_valid = is_valid && (appearance_bits5.eyebrow_style < 25); + is_valid = is_valid && (appearance_bits5.eyebrow_type < 25); is_valid = is_valid && (appearance_bits5.eyebrow_color < 8); is_valid = is_valid && (appearance_bits5.eyebrow_scale < 9); - is_valid = is_valid && (appearance_bits5.eyebrow_yscale < 7); - is_valid = is_valid && (appearance_bits5.eyebrow_rotation < 12); - is_valid = is_valid && (appearance_bits5.eyebrow_spacing < 12); - is_valid = is_valid && (appearance_bits5.eyebrow_y_position < 19); + is_valid = is_valid && (appearance_bits5.eyebrow_aspect < 7); + is_valid = is_valid && (appearance_bits5.eyebrow_rotate < 12); + is_valid = is_valid && (appearance_bits5.eyebrow_x < 12); + is_valid = is_valid && (appearance_bits5.eyebrow_y < 19); is_valid = is_valid && (appearance_bits6.nose_type < 18); is_valid = is_valid && (appearance_bits6.nose_scale < 9); - is_valid = is_valid && (appearance_bits6.nose_y_position < 19); + is_valid = is_valid && (appearance_bits6.nose_y < 19); is_valid = is_valid && (appearance_bits7.mouth_type < 36); is_valid = is_valid && (appearance_bits7.mouth_color < 5); is_valid = is_valid && (appearance_bits7.mouth_scale < 9); - is_valid = is_valid && (appearance_bits7.mouth_horizontal_stretch < 7); - is_valid = is_valid && (appearance_bits8.mouth_y_position < 19); + is_valid = is_valid && (appearance_bits7.mouth_aspect < 7); + is_valid = is_valid && (appearance_bits8.mouth_y < 19); is_valid = is_valid && (appearance_bits8.mustache_type < 6); is_valid = is_valid && (appearance_bits9.mustache_scale < 7); - is_valid = is_valid && (appearance_bits9.mustache_y_position < 17); + is_valid = is_valid && (appearance_bits9.mustache_y < 17); - is_valid = is_valid && (appearance_bits9.bear_type < 6); - is_valid = is_valid && (appearance_bits9.facial_hair_color < 8); + is_valid = is_valid && (appearance_bits9.beard_type < 6); + is_valid = is_valid && (appearance_bits9.beard_color < 8); - is_valid = is_valid && (appearance_bits10.glasses_type < 9); - is_valid = is_valid && (appearance_bits10.glasses_color < 6); - is_valid = is_valid && (appearance_bits10.glasses_scale < 8); - is_valid = is_valid && (appearance_bits10.glasses_y_position < 21); + is_valid = is_valid && (appearance_bits10.glass_type < 9); + is_valid = is_valid && (appearance_bits10.glass_color < 6); + is_valid = is_valid && (appearance_bits10.glass_scale < 8); + is_valid = is_valid && (appearance_bits10.glass_y < 21); - is_valid = is_valid && (appearance_bits11.mole_enabled < 2); + is_valid = is_valid && (appearance_bits11.mole_type < 2); is_valid = is_valid && (appearance_bits11.mole_scale < 9); - is_valid = is_valid && (appearance_bits11.mole_x_position < 17); - is_valid = is_valid && (appearance_bits11.mole_y_position < 31); + is_valid = is_valid && (appearance_bits11.mole_x < 17); + is_valid = is_valid && (appearance_bits11.mole_y < 31); return is_valid; } diff --git a/src/core/hle/service/mii/types/ver3_store_data.h b/src/core/hle/service/mii/types/ver3_store_data.h index 6b4e1eb9cd..11caeb5c3b 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.h +++ b/src/core/hle/service/mii/types/ver3_store_data.h @@ -6,20 +6,32 @@ #include "core/hle/service/mii/mii_types.h" namespace Service::Mii { -class CharInfo; +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: - // TODO: This function is wrong. It should use StoreData. - void BuildToStoreData(CharInfo& out_char_info) const; - // TODO: This function is wrong. It should use StoreData. - void BuildFromStoreData(const CharInfo& char_info); + void BuildToStoreData(StoreData& out_store_data) const; + void BuildFromStoreData(const StoreData& store_data); u32 IsValid() const; @@ -30,7 +42,7 @@ public: BitField<0, 1, u8> allow_copying; BitField<1, 1, u8> profanity_flag; BitField<2, 2, u8> region_lock; - BitField<4, 2, u8> character_set; + BitField<4, 2, u8> font_region; } region_information; u16_be mii_id; u64_be system_id; @@ -53,21 +65,21 @@ public: u8 raw; BitField<0, 1, u8> disable_sharing; - BitField<1, 4, u8> face_shape; - BitField<5, 3, u8> skin_color; + BitField<1, 4, u8> faceline_type; + BitField<5, 3, u8> faceline_color; } appearance_bits1; union { u8 raw; - BitField<0, 4, u8> wrinkles; - BitField<4, 4, u8> makeup; + BitField<0, 4, u8> faceline_wrinkle; + BitField<4, 4, u8> faceline_make; } appearance_bits2; - u8 hair_style; + u8 hair_type; union { u8 raw; BitField<0, 3, u8> hair_color; - BitField<3, 1, u8> flip_hair; + BitField<3, 1, u8> hair_flip; } appearance_bits3; union { u32 raw; @@ -75,28 +87,28 @@ public: 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; + 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_style; + BitField<0, 5, u32> eyebrow_type; 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; + 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_position; + BitField<9, 5, u16> nose_y; } appearance_bits6; union { u16 raw; @@ -104,38 +116,38 @@ public: 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; + BitField<13, 3, u16> mouth_aspect; } appearance_bits7; union { u8 raw; - BitField<0, 5, u8> mouth_y_position; + BitField<0, 5, u8> mouth_y; 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<0, 3, u16> beard_type; + BitField<3, 3, u16> beard_color; BitField<6, 4, u16> mustache_scale; - BitField<10, 5, u16> mustache_y_position; + BitField<10, 5, u16> mustache_y; } 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; + 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_enabled; + BitField<0, 1, u16> mole_type; BitField<1, 4, u16> mole_scale; - BitField<5, 5, u16> mole_x_position; - BitField<10, 5, u16> mole_y_position; + BitField<5, 5, u16> mole_x; + BitField<10, 5, u16> mole_y; } appearance_bits11; std::array author_name; diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 5e61d2595e..eb7706015e 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -824,8 +824,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) { @@ -834,8 +837,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe } SetAmiiboName(settings, register_info.amiibo_name); - tag_data.owner_mii.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; @@ -1452,7 +1455,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); @@ -1466,7 +1469,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.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); From bd169f417f471f574784fa3b499f57ad42cf1013 Mon Sep 17 00:00:00 2001 From: german77 Date: Mon, 11 Sep 2023 00:58:46 -0600 Subject: [PATCH 27/69] mii: Prepare Interface for new implementation --- .../service/am/applets/applet_mii_edit.cpp | 11 +- src/core/hle/service/mii/mii.cpp | 128 +++++++------- src/core/hle/service/mii/mii_manager.cpp | 162 +++++++++++------- src/core/hle/service/mii/mii_manager.h | 29 +++- src/core/hle/service/mii/mii_types.h | 10 ++ src/core/hle/service/nfc/common/device.cpp | 8 +- 6 files changed, 210 insertions(+), 138 deletions(-) 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 f8e2bac320..350a90818c 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/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 680f06bebe..653c367404 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -15,8 +15,8 @@ namespace Service::Mii { class IDatabaseService final : public ServiceFramework { 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"}, @@ -53,34 +53,27 @@ public: } private: - template - std::vector SerializeArray(const std::vector& values) { - std::vector 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()}; 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(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(is_full_database); } void GetCount(HLERequestContext& ctx) { @@ -89,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(manager.GetCount(source_flag)); + rb.Push(mii_count); } void Get(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto source_flag{rp.PopRaw()}; + const auto output_size{ctx.GetWriteBufferNumElements()}; - 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)}; - if (default_miis.size() > 0) { - ctx.WriteBuffer(SerializeArray(default_miis)); + u32 mii_count{}; + std::vector char_info_elements(output_size); + Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag); + + if (mii_count != 0) { + ctx.WriteBuffer(char_info_elements); } IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(default_miis.size())); + rb.Push(result); + rb.Push(mii_count); } void Get1(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto source_flag{rp.PopRaw()}; + const auto output_size{ctx.GetWriteBufferNumElements()}; - 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 char_info(output_size); + Result result = manager.Get(metadata, char_info, mii_count, source_flag); - std::vector values; - for (const auto& element : default_miis) { - values.emplace_back(element.char_info); + if (mii_count != 0) { + ctx.WriteBuffer(char_info); } - ctx.WriteBuffer(SerializeArray(values)); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(default_miis.size())); + rb.Push(result); + rb.Push(mii_count); } void UpdateLatest(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto info{rp.PopRaw()}; + const auto char_info{rp.PopRaw()}; const auto source_flag{rp.PopRaw()}; 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; @@ -152,7 +151,6 @@ private: void BuildRandom(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto age{rp.PopRaw()}; const auto gender{rp.PopRaw()}; const auto race{rp.PopRaw()}; @@ -162,46 +160,47 @@ private: if (age > Age::All) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultInvalidArgument); - LOG_ERROR(Service_Mii, "invalid age={}", age); return; } if (gender > Gender::All) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultInvalidArgument); - LOG_ERROR(Service_Mii, "invalid gender={}", gender); return; } if (race > Race::All) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultInvalidArgument); - LOG_ERROR(Service_Mii, "invalid race={}", race); 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(manager.BuildRandom(age, gender, race)); + rb.PushRaw(char_info); } void BuildDefault(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto index{rp.Pop()}; - 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(ResultInvalidArgument); return; } + CharInfo char_info{}; + manager.BuildDefault(char_info, index); + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw(manager.BuildDefault(index)); + rb.PushRaw(char_info); } void GetIndex(HLERequestContext& ctx) { @@ -210,19 +209,21 @@ private: LOG_DEBUG(Service_Mii, "called"); - u32 index{}; + s32 index{}; + const Result 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(); + const auto interface_version{rp.PopRaw()}; - 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); @@ -230,30 +231,27 @@ private: void Convert(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto mii_v3{rp.PopRaw()}; 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(manager.ConvertV3ToCharInfo(mii_v3)); + rb.PushRaw(char_info); } - constexpr bool IsInterfaceVersionSupported(u32 interface_version) const { - return current_interface_version >= interface_version; - } - - MiiManager manager; - - u32 current_interface_version{}; - u64 current_update_counter{}; + MiiManager manager{}; + DatabaseSessionMetadata metadata{}; + bool is_system{}; }; class MiiDBModule final : public ServiceFramework { 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"}, @@ -267,10 +265,12 @@ private: void GetDatabaseService(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system); + rb.PushIpcInterface(system, is_system); LOG_DEBUG(Service_Mii, "called"); } + + bool is_system{}; }; class MiiImg final : public ServiceFramework { @@ -302,8 +302,10 @@ public: void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); - server_manager->RegisterNamedService("mii:e", std::make_shared(system, "mii:e")); - server_manager->RegisterNamedService("mii:u", std::make_shared(system, "mii:u")); + server_manager->RegisterNamedService("mii:e", + std::make_shared(system, "mii:e", true)); + server_manager->RegisterNamedService("mii:u", + std::make_shared(system, "mii:u", false)); server_manager->RegisterNamedService("miiimg", std::make_shared(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 3951e0b9c8..153a484c08 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -16,45 +16,18 @@ #include "core/hle/service/mii/types/raw_data.h" namespace Service::Mii { - -namespace { - constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; -CharInfo ConvertStoreDataToInfo(const StoreData& data) { - CharInfo char_info{}; - char_info.SetFromStoreData(data); - return char_info; -} +MiiManager::MiiManager() {} -StoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { - StoreData store_data{}; - store_data.BuildRandom(age, gender, race); - - return store_data; -} - -StoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { - StoreData store_data{}; - store_data.BuildDefault(0); - - return store_data; -} - -} // namespace - -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 { @@ -62,19 +35,19 @@ bool MiiManager::IsFullDatabase() const { return false; } -u32 MiiManager::GetCount(SourceFlag source_flag) const { - std::size_t count{}; - if ((source_flag & SourceFlag::Database) != SourceFlag::None) { +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(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 ResultNotFound; } @@ -83,48 +56,117 @@ Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, Source 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 { - CharInfo char_info{}; +void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const { StoreData store_data{}; mii_v3.BuildToStoreData(store_data); - char_info.SetFromStoreData(store_data); - return char_info; + out_char_info.SetFromStoreData(store_data); } -std::vector MiiManager::GetDefault(SourceFlag source_flag) { - std::vector result; +Result MiiManager::Get(const DatabaseSessionMetadata& metadata, + std::span out_elements, u32& out_count, + SourceFlag source_flag) { + if ((source_flag & SourceFlag::Database) == SourceFlag::None) { + return BuildDefault(out_elements, out_count, source_flag); + } + // TODO(bunnei): We don't implement the Mii database, so we can't have an entry + + // Include default Mii at the end of the list + return BuildDefault(out_elements, out_count, source_flag); +} + +Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span 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); + } + + // TODO(bunnei): We don't implement the Mii database, so we can't have an entry + + // Include default Mii at the end of the list + return BuildDefault(out_char_info, out_count, source_flag); +} + +Result MiiManager::BuildDefault(std::span out_elements, u32& out_count, + SourceFlag source_flag) { if ((source_flag & SourceFlag::Default) == SourceFlag::None) { - return result; + return ResultSuccess; } - for (std::size_t index = 0; index < DefaultMiiCount; index++) { - result.emplace_back(BuildDefault(index), Source::Default); + StoreData store_data{}; + + for (std::size_t index = 0; index < DefaultMiiCount; ++index) { + if (out_elements.size() <= static_cast(out_count)) { + return ResultInvalidArgumentSize; + } + + store_data.BuildDefault(static_cast(index)); + + out_elements[out_count].source = Source::Default; + out_elements[out_count].char_info.SetFromStoreData(store_data); + out_count++; } - return result; + return ResultSuccess; } -Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) { +Result MiiManager::BuildDefault(std::span out_char_info, u32& out_count, + SourceFlag source_flag) { + if ((source_flag & SourceFlag::Default) == SourceFlag::None) { + return ResultSuccess; + } + + StoreData store_data{}; + + for (std::size_t index = 0; index < DefaultMiiCount; ++index) { + if (out_char_info.size() <= static_cast(out_count)) { + return ResultInvalidArgumentSize; + } + + store_data.BuildDefault(static_cast(index)); + + out_char_info[out_count].SetFromStoreData(store_data); + out_count++; + } + + return ResultSuccess; +} + +Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, + s32& out_index) { + + if (char_info.Verify() != 0) { + return ResultInvalidCharInfo; + } + 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 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 6098474e97..4f8be06c35 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -19,16 +19,24 @@ 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; + 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 out_elements, + u32& out_count, SourceFlag source_flag); + Result Get(const DatabaseSessionMetadata& metadata, std::span 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 GetDefault(SourceFlag source_flag); - Result GetIndex(const CharInfo& info, u32& index); + Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, + s32& out_index); + void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version); struct MiiDatabase { u32 magic{}; // 'NFDB' @@ -40,7 +48,10 @@ public: static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); private: - const Common::UUID user_id{}; + Result BuildDefault(std::span out_elements, u32& out_count, + SourceFlag source_flag); + Result BuildDefault(std::span out_char_info, u32& out_count, SourceFlag source_flag); + u64 update_counter{}; }; diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h index d62005f614..b23ce477da 100644 --- a/src/core/hle/service/mii/mii_types.h +++ b/src/core/hle/service/mii/mii_types.h @@ -165,4 +165,14 @@ struct DefaultMii { }; static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData 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/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index eb7706015e..5dda123438 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -680,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, From ec25f847d8c066241d3aa9bb00bd11cb0c47b161 Mon Sep 17 00:00:00 2001 From: german77 Date: Mon, 11 Sep 2023 08:53:23 -0600 Subject: [PATCH 28/69] mii: service: Address review --- src/core/hle/service/mii/mii.cpp | 2 +- src/core/hle/service/mii/mii_manager.cpp | 6 +- src/core/hle/service/mii/mii_manager.h | 9 -- src/core/hle/service/mii/mii_types.h | 57 ++++++++- src/core/hle/service/mii/mii_util.h | 3 +- src/core/hle/service/mii/types/char_info.cpp | 112 +++++++++--------- src/core/hle/service/mii/types/char_info.h | 2 +- src/core/hle/service/mii/types/core_data.cpp | 12 +- src/core/hle/service/mii/types/raw_data.cpp | 2 +- src/core/hle/service/mii/types/raw_data.h | 2 +- .../hle/service/mii/types/ver3_store_data.cpp | 23 +--- .../hle/service/mii/types/ver3_store_data.h | 6 +- src/yuzu/applets/qt_amiibo_settings.cpp | 3 +- 13 files changed, 135 insertions(+), 104 deletions(-) diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 653c367404..3b83c5ed7d 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -210,7 +210,7 @@ private: LOG_DEBUG(Service_Mii, "called"); s32 index{}; - const Result result = manager.GetIndex(metadata, info, index); + const auto result = manager.GetIndex(metadata, info, index); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(result); diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 153a484c08..292d637773 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -37,10 +37,10 @@ bool MiiManager::IsFullDatabase() const { u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const { u32 mii_count{}; - if ((source_flag & SourceFlag::Default) == SourceFlag::None) { + if ((source_flag & SourceFlag::Default) != SourceFlag::None) { mii_count += DefaultMiiCount; } - if ((source_flag & SourceFlag::Database) == SourceFlag::None) { + if ((source_flag & SourceFlag::Database) != SourceFlag::None) { // TODO(bunnei): We don't implement the Mii database, but when we do, update this } return mii_count; @@ -153,7 +153,7 @@ Result MiiManager::BuildDefault(std::span out_char_info, u32& out_coun Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, s32& out_index) { - if (char_info.Verify() != 0) { + if (char_info.Verify() != ValidationResult::NoErrors) { return ResultInvalidCharInfo; } diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 4f8be06c35..a2e7a6d734 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -38,15 +38,6 @@ public: s32& out_index); void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version); - struct MiiDatabase { - u32 magic{}; // 'NFDB' - std::array miis{}; - INSERT_PADDING_BYTES(1); - u8 count{}; - u16 crc{}; - }; - static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); - private: Result BuildDefault(std::span out_elements, u32& out_count, SourceFlag source_flag); diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h index b23ce477da..8fc827b5ae 100644 --- a/src/core/hle/service/mii/mii_types.h +++ b/src/core/hle/service/mii/mii_types.h @@ -86,6 +86,61 @@ enum class SourceFlag : u32 { }; 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 data; @@ -163,7 +218,7 @@ struct DefaultMii { u32 type{}; Nickname nickname; }; -static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); +static_assert(sizeof(DefaultMii) == 0xd8, "DefaultMii has incorrect size."); struct DatabaseSessionMetadata { u32 interface_version; diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h index 782ffe22ff..ddb544c23d 100644 --- a/src/core/hle/service/mii/mii_util.h +++ b/src/core/hle/service/mii/mii_util.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "common/common_types.h" #include "common/swap.h" @@ -51,7 +52,7 @@ public: } static bool IsFontRegionValid(FontRegion font, std::span text) { - // Todo:: This function needs to check against the font tables + // TODO: This function needs to check against the font tables return true; } }; diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp index 0f9c37b847..cd7154c91d 100644 --- a/src/core/hle/service/mii/types/char_info.cpp +++ b/src/core/hle/service/mii/types/char_info.cpp @@ -62,161 +62,161 @@ void CharInfo::SetFromStoreData(const StoreData& store_data) { padding = '\0'; } -u32 CharInfo::Verify() const { +ValidationResult CharInfo::Verify() const { if (!create_id.IsValid()) { - return 0x32; + return ValidationResult::InvalidCreateId; } if (!name.IsValid()) { - return 0x33; + return ValidationResult::InvalidName; } if (3 < font_region) { - return 0x17; + return ValidationResult::InvalidFont; } if (0xb < favorite_color) { - return 0x16; + return ValidationResult::InvalidColor; } if (1 < gender) { - return 0x18; + return ValidationResult::InvalidGender; } - if (height < 0) { - return 0x20; + if (height > 0x7f) { + return ValidationResult::InvalidHeight; } - if (build < 0) { - return 3; + if (build > 0x7f) { + return ValidationResult::InvalidBuild; } if (1 < type) { - return 0x35; + return ValidationResult::InvalidType; } if (3 < region_move) { - return 0x31; + return ValidationResult::InvalidRegionMove; } if (0xb < faceline_type) { - return 0x15; + return ValidationResult::InvalidFacelineType; } if (9 < faceline_color) { - return 0x12; + return ValidationResult::InvalidFacelineColor; } if (0xb < faceline_wrinkle) { - return 0x14; + return ValidationResult::InvalidFacelineWrinkle; } if (0xb < faceline_make) { - return 0x13; + return ValidationResult::InvalidFacelineMake; } if (0x83 < hair_type) { - return 0x1f; + return ValidationResult::InvalidHairType; } if (99 < hair_color) { - return 0x1d; + return ValidationResult::InvalidHairColor; } if (1 < hair_flip) { - return 0x1e; + return ValidationResult::InvalidHairFlip; } if (0x3b < eye_type) { - return 8; + return ValidationResult::InvalidEyeType; } if (99 < eye_color) { - return 5; + return ValidationResult::InvalidEyeColor; } if (7 < eye_scale) { - return 7; + return ValidationResult::InvalidEyeScale; } if (6 < eye_aspect) { - return 4; + return ValidationResult::InvalidEyeAspect; } if (7 < eye_rotate) { - return 6; + return ValidationResult::InvalidEyeRotate; } if (0xc < eye_x) { - return 9; + return ValidationResult::InvalidEyeX; } if (0x12 < eye_y) { - return 10; + return ValidationResult::InvalidEyeY; } if (0x17 < eyebrow_type) { - return 0xf; + return ValidationResult::InvalidEyebrowType; } if (99 < eyebrow_color) { - return 0xc; + return ValidationResult::InvalidEyebrowColor; } if (8 < eyebrow_scale) { - return 0xe; + return ValidationResult::InvalidEyebrowScale; } if (6 < eyebrow_aspect) { - return 0xb; + return ValidationResult::InvalidEyebrowAspect; } if (0xb < eyebrow_rotate) { - return 0xd; + return ValidationResult::InvalidEyebrowRotate; } if (0xc < eyebrow_x) { - return 0x10; + return ValidationResult::InvalidEyebrowX; } if (0xf < eyebrow_y - 3) { - return 0x11; + return ValidationResult::InvalidEyebrowY; } if (0x11 < nose_type) { - return 0x2f; + return ValidationResult::InvalidNoseType; } if (nose_scale >= 9) { - return 0x2e; + return ValidationResult::InvalidNoseScale; } if (0x12 < nose_y) { - return 0x30; + return ValidationResult::InvalidNoseY; } if (0x23 < mouth_type) { - return 0x28; + return ValidationResult::InvalidMouthType; } if (99 < mouth_color) { - return 0x26; + return ValidationResult::InvalidMouthColor; } if (8 < mouth_scale) { - return 0x27; + return ValidationResult::InvalidMouthScale; } if (6 < mouth_aspect) { - return 0x25; + return ValidationResult::InvalidMouthAspect; } if (0x12 < mouth_y) { - return 0x29; + return ValidationResult::InvalidMoleY; } if (99 < beard_color) { - return 1; + return ValidationResult::InvalidBeardColor; } if (5 < beard_type) { - return 2; + return ValidationResult::InvalidBeardType; } if (5 < mustache_type) { - return 0x2b; + return ValidationResult::InvalidMustacheType; } if (8 < mustache_scale) { - return 0x2a; + return ValidationResult::InvalidMustacheScale; } if (0x10 < mustache_y) { - return 0x2c; + return ValidationResult::InvalidMustacheY; } if (0x13 < glasses_type) { - return 0x1b; + return ValidationResult::InvalidGlassType; } if (99 < glasses_color) { - return 0x19; + return ValidationResult::InvalidGlassColor; } if (7 < glasses_scale) { - return 0x1a; + return ValidationResult::InvalidGlassScale; } if (0x14 < glasses_y) { - return 0x1c; + return ValidationResult::InvalidGlassY; } if (mole_type >= 2) { - return 0x22; + return ValidationResult::InvalidMoleType; } if (8 < mole_scale) { - return 0x21; + return ValidationResult::InvalidMoleScale; } if (mole_x >= 0x11) { - return 0x23; + return ValidationResult::InvalidMoleX; } if (0x1e < mole_y) { - return 0x24; + return ValidationResult::InvalidMoleY; } - return 0; + return ValidationResult::NoErrors; } Common::UUID CharInfo::GetCreateId() const { @@ -424,7 +424,7 @@ u8 CharInfo::GetMoleY() const { } bool CharInfo::operator==(const CharInfo& info) { - bool is_identical = info.Verify() == 0; + 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(); diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h index 4f70edc248..65a7707d36 100644 --- a/src/core/hle/service/mii/types/char_info.h +++ b/src/core/hle/service/mii/types/char_info.h @@ -13,7 +13,7 @@ class CharInfo { public: void SetFromStoreData(const StoreData& store_data_raw); - u32 Verify() const; + ValidationResult Verify() const; Common::UUID GetCreateId() const; Nickname GetNickname() const; diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp index 9d7e604a94..de82481b0f 100644 --- a/src/core/hle/service/mii/types/core_data.cpp +++ b/src/core/hle/service/mii/types/core_data.cpp @@ -26,10 +26,10 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) { data.build.Assign(64); if (age == Age::All) { - const auto temp{MiiUtil::GetRandomValue(10)}; - if (temp >= 8) { + const auto random{MiiUtil::GetRandomValue(10)}; + if (random >= 8) { age = Age::Old; - } else if (temp >= 4) { + } else if (random >= 4) { age = Age::Normal; } else { age = Age::Young; @@ -37,10 +37,10 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) { } if (race == Race::All) { - const auto temp{MiiUtil::GetRandomValue(10)}; - if (temp >= 8) { + const auto random{MiiUtil::GetRandomValue(10)}; + if (random >= 8) { race = Race::Black; - } else if (temp >= 4) { + } else if (random >= 4) { race = Race::White; } else { race = Race::Asian; diff --git a/src/core/hle/service/mii/types/raw_data.cpp b/src/core/hle/service/mii/types/raw_data.cpp index ef678c5276..ed299521f8 100644 --- a/src/core/hle/service/mii/types/raw_data.cpp +++ b/src/core/hle/service/mii/types/raw_data.cpp @@ -5,7 +5,7 @@ namespace Service::Mii::RawData { -constexpr std::array FromVer3FacelineColorTable{ +constexpr std::array FromVer3FacelineColorTable{ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, }; diff --git a/src/core/hle/service/mii/types/raw_data.h b/src/core/hle/service/mii/types/raw_data.h index 180f49fd0d..8c910096f7 100644 --- a/src/core/hle/service/mii/types/raw_data.h +++ b/src/core/hle/service/mii/types/raw_data.h @@ -10,7 +10,7 @@ namespace Service::Mii::RawData { struct RandomMiiValues { - std::array values{}; + std::array values{}; }; static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index 53a3fe44bc..c7624520c1 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -33,16 +33,7 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { out_store_data.SetHeight(height); out_store_data.SetBuild(build); - // Copy name until string terminator - Nickname name = {}; - for (std::size_t index = 0; index < name.data.size() - 1; index++) { - name.data[index] = mii_name[index]; - if (name.data[index] == 0) { - break; - } - } - - out_store_data.SetNickname(name); + out_store_data.SetNickname(mii_name); out_store_data.SetFontRegion( static_cast(static_cast(region_information.font_region))); @@ -108,15 +99,7 @@ void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { height = store_data.GetHeight(); build = store_data.GetBuild(); - // Copy name until string terminator - mii_name = {}; - for (std::size_t index = 0; index < store_data.GetNickname().data.size() - 1; index++) { - mii_name[index] = store_data.GetNickname().data[index]; - if (mii_name[index] == 0) { - break; - } - } - + mii_name = store_data.GetNickname(); region_information.font_region.Assign(static_cast(store_data.GetFontRegion())); appearance_bits1.faceline_type.Assign(store_data.GetFacelineType()); @@ -183,7 +166,7 @@ void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { u32 Ver3StoreData::IsValid() const { bool is_valid = version == 0 || version == 3; - is_valid = is_valid && (mii_name[0] != 0); + 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); diff --git a/src/core/hle/service/mii/types/ver3_store_data.h b/src/core/hle/service/mii/types/ver3_store_data.h index 11caeb5c3b..47907bf7d7 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.h +++ b/src/core/hle/service/mii/types/ver3_store_data.h @@ -47,7 +47,7 @@ public: u16_be mii_id; u64_be system_id; u32_be specialness_and_creation_date; - std::array creator_mac; + std::array creator_mac; u16_be padding; union { u16 raw; @@ -58,7 +58,7 @@ public: BitField<10, 4, u16> favorite_color; BitField<14, 1, u16> favorite; } mii_information; - std::array mii_name; + Nickname mii_name; u8 height; u8 build; union { @@ -150,7 +150,7 @@ public: BitField<10, 5, u16> mole_y; } appearance_bits11; - std::array author_name; + Nickname author_name; INSERT_PADDING_BYTES(0x2); u16_be crc; }; diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp index 4988fcc834..b457a736ab 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); From 66f2947854f993f9d907ddc9c23f7e28aa12f330 Mon Sep 17 00:00:00 2001 From: liamwhite Date: Mon, 11 Sep 2023 17:25:21 -0400 Subject: [PATCH 29/69] ci: fix msvc when used with LTO (#11459) --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 95d54dadcc..d7f68618c6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,6 +85,7 @@ if (MSVC) /wd4100 # 'identifier': unreferenced formal parameter /wd4324 # 'struct_name': structure was padded due to __declspec(align()) /wd4201 # nonstandard extension used : nameless struct/union + /wd4702 # unreachable code (when used with LTO) ) if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS) From 1b6852a36cccd6ab6886f6d5d603d12898b2eda8 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Tue, 12 Sep 2023 00:20:42 +0200 Subject: [PATCH 30/69] bsd: Demote "Select" log to dehug This is very spammy in Minecraft Legends. --- src/core/hle/service/sockets/bsd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index d8509c1dda..85849d5f37 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -170,7 +170,7 @@ void BSD::Socket(HLERequestContext& ctx) { } void BSD::Select(HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_DEBUG(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; From a2150e456c15d11c9d06604a91dd393d99b586c1 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Tue, 12 Sep 2023 00:21:03 +0200 Subject: [PATCH 31/69] am: Stub GetSaveDataSizeMax Needed for Minecraft Legends. --- src/core/hle/service/am/am.cpp | 14 +++++++++++++- src/core/hle/service/am/am.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index e92f400de2..4f810d202f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1386,7 +1386,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"}, - {28, nullptr, "GetSaveDataSizeMax"}, + {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"}, {29, nullptr, "GetCacheStorageMax"}, {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, @@ -1824,6 +1824,18 @@ void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) { rb.PushRaw(resp); } +void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + constexpr u64 size_max_normal = 0xFFFFFFF; + constexpr u64 size_max_journal = 0xFFFFFFF; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(size_max_normal); + rb.Push(size_max_journal); +} + void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index d68998f049..d3c2e55cec 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -316,6 +316,7 @@ private: void ExtendSaveData(HLERequestContext& ctx); void GetSaveDataSize(HLERequestContext& ctx); void CreateCacheStorage(HLERequestContext& ctx); + void GetSaveDataSizeMax(HLERequestContext& ctx); void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); void BeginBlockingHomeButton(HLERequestContext& ctx); From 4d138b760b1eb09ee59dca40dba86112e3c8a39d Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Mon, 11 Sep 2023 17:12:51 -0600 Subject: [PATCH 32/69] service: mii: Remove most magic values --- src/core/hle/service/mii/mii_types.h | 559 +++++++++- src/core/hle/service/mii/types/char_info.cpp | 170 +-- src/core/hle/service/mii/types/char_info.h | 96 +- src/core/hle/service/mii/types/core_data.cpp | 272 +++-- src/core/hle/service/mii/types/core_data.h | 91 +- src/core/hle/service/mii/types/raw_data.cpp | 976 +++++++++--------- src/core/hle/service/mii/types/raw_data.h | 16 +- src/core/hle/service/mii/types/store_data.cpp | 138 +-- src/core/hle/service/mii/types/store_data.h | 78 +- .../hle/service/mii/types/ver3_store_data.cpp | 204 ++-- 10 files changed, 1534 insertions(+), 1066 deletions(-) diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h index 8fc827b5ae..95476f7456 100644 --- a/src/core/hle/service/mii/mii_types.h +++ b/src/core/hle/service/mii/mii_types.h @@ -13,20 +13,517 @@ namespace Service::Mii { -enum class Age : u32 { +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, + All, // Default + + Max = All, }; -enum class BeardType : u32 { +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, - Beard1, - Beard2, - Beard3, - Beard4, - Beard5, + 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 { @@ -36,42 +533,6 @@ enum class BeardAndMustacheFlag : u32 { }; 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, @@ -173,7 +634,7 @@ struct DefaultMii { u32 face_makeup{}; u32 hair_type{}; u32 hair_color{}; - HairFlip hair_flip{}; + u32 hair_flip{}; u32 eye_type{}; u32 eye_color{}; u32 eye_scale{}; @@ -196,8 +657,8 @@ struct DefaultMii { u32 mouth_scale{}; u32 mouth_aspect{}; u32 mouth_y{}; - MustacheType mustache_type{}; - BeardType beard_type{}; + u32 mustache_type{}; + u32 beard_type{}; u32 beard_color{}; u32 mustache_scale{}; u32 mustache_y{}; @@ -211,10 +672,10 @@ struct DefaultMii { u32 mole_y{}; u32 height{}; u32 weight{}; - Gender gender{}; + u32 gender{}; u32 favorite_color{}; u32 region_move{}; - FontRegion font_region{}; + u32 font_region{}; u32 type{}; Nickname nickname; }; diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp index cd7154c91d..bb948c6281 100644 --- a/src/core/hle/service/mii/types/char_info.cpp +++ b/src/core/hle/service/mii/types/char_info.cpp @@ -10,7 +10,7 @@ void CharInfo::SetFromStoreData(const StoreData& store_data) { name = store_data.GetNickname(); null_terminator = '\0'; create_id = store_data.GetCreateId(); - font_region = static_cast(store_data.GetFontRegion()); + font_region = store_data.GetFontRegion(); favorite_color = store_data.GetFavoriteColor(); gender = store_data.GetGender(); height = store_data.GetHeight(); @@ -51,10 +51,10 @@ void CharInfo::SetFromStoreData(const StoreData& store_data) { mustache_type = store_data.GetMustacheType(); mustache_scale = store_data.GetMustacheScale(); mustache_y = store_data.GetMustacheY(); - glasses_type = store_data.GetGlassType(); - glasses_color = store_data.GetGlassColor(); - glasses_scale = store_data.GetGlassScale(); - glasses_y = store_data.GetGlassY(); + 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(); @@ -69,151 +69,151 @@ ValidationResult CharInfo::Verify() const { if (!name.IsValid()) { return ValidationResult::InvalidName; } - if (3 < font_region) { + if (font_region > FontRegion::Max) { return ValidationResult::InvalidFont; } - if (0xb < favorite_color) { + if (favorite_color > FavoriteColor::Max) { return ValidationResult::InvalidColor; } - if (1 < gender) { + if (gender > Gender::Max) { return ValidationResult::InvalidGender; } - if (height > 0x7f) { + if (height > MaxHeight) { return ValidationResult::InvalidHeight; } - if (build > 0x7f) { + if (build > MaxBuild) { return ValidationResult::InvalidBuild; } - if (1 < type) { + if (type > MaxType) { return ValidationResult::InvalidType; } - if (3 < region_move) { + if (region_move > MaxRegionMove) { return ValidationResult::InvalidRegionMove; } - if (0xb < faceline_type) { + if (faceline_type > FacelineType::Max) { return ValidationResult::InvalidFacelineType; } - if (9 < faceline_color) { + if (faceline_color > FacelineColor::Max) { return ValidationResult::InvalidFacelineColor; } - if (0xb < faceline_wrinkle) { + if (faceline_wrinkle > FacelineWrinkle::Max) { return ValidationResult::InvalidFacelineWrinkle; } - if (0xb < faceline_make) { + if (faceline_make > FacelineMake::Max) { return ValidationResult::InvalidFacelineMake; } - if (0x83 < hair_type) { + if (hair_type > HairType::Max) { return ValidationResult::InvalidHairType; } - if (99 < hair_color) { + if (hair_color > CommonColor::Max) { return ValidationResult::InvalidHairColor; } - if (1 < hair_flip) { + if (hair_flip > HairFlip::Max) { return ValidationResult::InvalidHairFlip; } - if (0x3b < eye_type) { + if (eye_type > EyeType::Max) { return ValidationResult::InvalidEyeType; } - if (99 < eye_color) { + if (eye_color > CommonColor::Max) { return ValidationResult::InvalidEyeColor; } - if (7 < eye_scale) { + if (eye_scale > MaxEyeScale) { return ValidationResult::InvalidEyeScale; } - if (6 < eye_aspect) { + if (eye_aspect > MaxEyeAspect) { return ValidationResult::InvalidEyeAspect; } - if (7 < eye_rotate) { + if (eye_rotate > MaxEyeX) { return ValidationResult::InvalidEyeRotate; } - if (0xc < eye_x) { + if (eye_x > MaxEyeX) { return ValidationResult::InvalidEyeX; } - if (0x12 < eye_y) { + if (eye_y > MaxEyeY) { return ValidationResult::InvalidEyeY; } - if (0x17 < eyebrow_type) { + if (eyebrow_type > EyebrowType::Max) { return ValidationResult::InvalidEyebrowType; } - if (99 < eyebrow_color) { + if (eyebrow_color > CommonColor::Max) { return ValidationResult::InvalidEyebrowColor; } - if (8 < eyebrow_scale) { + if (eyebrow_scale > MaxEyebrowScale) { return ValidationResult::InvalidEyebrowScale; } - if (6 < eyebrow_aspect) { + if (eyebrow_aspect > MaxEyebrowAspect) { return ValidationResult::InvalidEyebrowAspect; } - if (0xb < eyebrow_rotate) { + if (eyebrow_rotate > MaxEyebrowRotate) { return ValidationResult::InvalidEyebrowRotate; } - if (0xc < eyebrow_x) { + if (eyebrow_x > MaxEyebrowX) { return ValidationResult::InvalidEyebrowX; } - if (0xf < eyebrow_y - 3) { + if (eyebrow_y > MaxEyebrowY) { return ValidationResult::InvalidEyebrowY; } - if (0x11 < nose_type) { + if (nose_type > NoseType::Max) { return ValidationResult::InvalidNoseType; } - if (nose_scale >= 9) { + if (nose_scale > MaxNoseScale) { return ValidationResult::InvalidNoseScale; } - if (0x12 < nose_y) { + if (nose_y > MaxNoseY) { return ValidationResult::InvalidNoseY; } - if (0x23 < mouth_type) { + if (mouth_type > MouthType::Max) { return ValidationResult::InvalidMouthType; } - if (99 < mouth_color) { + if (mouth_color > CommonColor::Max) { return ValidationResult::InvalidMouthColor; } - if (8 < mouth_scale) { + if (mouth_scale > MaxMouthScale) { return ValidationResult::InvalidMouthScale; } - if (6 < mouth_aspect) { + if (mouth_aspect > MaxMoutAspect) { return ValidationResult::InvalidMouthAspect; } - if (0x12 < mouth_y) { + if (mouth_y > MaxMouthY) { return ValidationResult::InvalidMoleY; } - if (99 < beard_color) { + if (beard_color > CommonColor::Max) { return ValidationResult::InvalidBeardColor; } - if (5 < beard_type) { + if (beard_type > BeardType::Max) { return ValidationResult::InvalidBeardType; } - if (5 < mustache_type) { + if (mustache_type > MustacheType::Max) { return ValidationResult::InvalidMustacheType; } - if (8 < mustache_scale) { + if (mustache_scale > MaxMustacheScale) { return ValidationResult::InvalidMustacheScale; } - if (0x10 < mustache_y) { + if (mustache_y > MasMustacheY) { return ValidationResult::InvalidMustacheY; } - if (0x13 < glasses_type) { + if (glass_type > GlassType::Max) { return ValidationResult::InvalidGlassType; } - if (99 < glasses_color) { + if (glass_color > CommonColor::Max) { return ValidationResult::InvalidGlassColor; } - if (7 < glasses_scale) { + if (glass_scale > MaxGlassScale) { return ValidationResult::InvalidGlassScale; } - if (0x14 < glasses_y) { + if (glass_y > MaxGlassY) { return ValidationResult::InvalidGlassY; } - if (mole_type >= 2) { + if (mole_type > MoleType::Max) { return ValidationResult::InvalidMoleType; } - if (8 < mole_scale) { + if (mole_scale > MaxMoleScale) { return ValidationResult::InvalidMoleScale; } - if (mole_x >= 0x11) { + if (mole_x > MaxMoleX) { return ValidationResult::InvalidMoleX; } - if (0x1e < mole_y) { + if (mole_y > MaxMoleY) { return ValidationResult::InvalidMoleY; } return ValidationResult::NoErrors; @@ -227,15 +227,15 @@ Nickname CharInfo::GetNickname() const { return name; } -u8 CharInfo::GetFontRegion() const { +FontRegion CharInfo::GetFontRegion() const { return font_region; } -u8 CharInfo::GetFavoriteColor() const { +FavoriteColor CharInfo::GetFavoriteColor() const { return favorite_color; } -u8 CharInfo::GetGender() const { +Gender CharInfo::GetGender() const { return gender; } @@ -255,39 +255,39 @@ u8 CharInfo::GetRegionMove() const { return region_move; } -u8 CharInfo::GetFacelineType() const { +FacelineType CharInfo::GetFacelineType() const { return faceline_type; } -u8 CharInfo::GetFacelineColor() const { +FacelineColor CharInfo::GetFacelineColor() const { return faceline_color; } -u8 CharInfo::GetFacelineWrinkle() const { +FacelineWrinkle CharInfo::GetFacelineWrinkle() const { return faceline_wrinkle; } -u8 CharInfo::GetFacelineMake() const { +FacelineMake CharInfo::GetFacelineMake() const { return faceline_make; } -u8 CharInfo::GetHairType() const { +HairType CharInfo::GetHairType() const { return hair_type; } -u8 CharInfo::GetHairColor() const { +CommonColor CharInfo::GetHairColor() const { return hair_color; } -u8 CharInfo::GetHairFlip() const { +HairFlip CharInfo::GetHairFlip() const { return hair_flip; } -u8 CharInfo::GetEyeType() const { +EyeType CharInfo::GetEyeType() const { return eye_type; } -u8 CharInfo::GetEyeColor() const { +CommonColor CharInfo::GetEyeColor() const { return eye_color; } @@ -311,11 +311,11 @@ u8 CharInfo::GetEyeY() const { return eye_y; } -u8 CharInfo::GetEyebrowType() const { +EyebrowType CharInfo::GetEyebrowType() const { return eyebrow_type; } -u8 CharInfo::GetEyebrowColor() const { +CommonColor CharInfo::GetEyebrowColor() const { return eyebrow_color; } @@ -339,7 +339,7 @@ u8 CharInfo::GetEyebrowY() const { return eyebrow_y; } -u8 CharInfo::GetNoseType() const { +NoseType CharInfo::GetNoseType() const { return nose_type; } @@ -351,11 +351,11 @@ u8 CharInfo::GetNoseY() const { return nose_y; } -u8 CharInfo::GetMouthType() const { +MouthType CharInfo::GetMouthType() const { return mouth_type; } -u8 CharInfo::GetMouthColor() const { +CommonColor CharInfo::GetMouthColor() const { return mouth_color; } @@ -371,15 +371,15 @@ u8 CharInfo::GetMouthY() const { return mouth_y; } -u8 CharInfo::GetBeardColor() const { +CommonColor CharInfo::GetBeardColor() const { return beard_color; } -u8 CharInfo::GetBeardType() const { +BeardType CharInfo::GetBeardType() const { return beard_type; } -u8 CharInfo::GetMustacheType() const { +MustacheType CharInfo::GetMustacheType() const { return mustache_type; } @@ -391,23 +391,23 @@ u8 CharInfo::GetMustacheY() const { return mustache_y; } -u8 CharInfo::GetGlassType() const { - return glasses_type; +GlassType CharInfo::GetGlassType() const { + return glass_type; } -u8 CharInfo::GetGlassColor() const { - return glasses_color; +CommonColor CharInfo::GetGlassColor() const { + return glass_color; } u8 CharInfo::GetGlassScale() const { - return glasses_scale; + return glass_scale; } u8 CharInfo::GetGlassY() const { - return glasses_y; + return glass_y; } -u8 CharInfo::GetMoleType() const { +MoleType CharInfo::GetMoleType() const { return mole_type; } @@ -468,10 +468,10 @@ bool CharInfo::operator==(const CharInfo& info) { is_identical &= mustache_type == info.GetMustacheType(); is_identical &= mustache_scale == info.GetMustacheScale(); is_identical &= mustache_y == info.GetMustacheY(); - is_identical &= glasses_type == info.GetGlassType(); - is_identical &= glasses_color == info.GetGlassColor(); - is_identical &= glasses_scale == info.GetGlassScale(); - is_identical &= glasses_y == info.GetGlassY(); + 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(); diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h index 65a7707d36..d069b221f5 100644 --- a/src/core/hle/service/mii/types/char_info.h +++ b/src/core/hle/service/mii/types/char_info.h @@ -17,52 +17,52 @@ public: Common::UUID GetCreateId() const; Nickname GetNickname() const; - u8 GetFontRegion() const; - u8 GetFavoriteColor() const; - u8 GetGender() const; + FontRegion GetFontRegion() const; + FavoriteColor GetFavoriteColor() const; + Gender GetGender() const; u8 GetHeight() const; u8 GetBuild() const; u8 GetType() const; u8 GetRegionMove() const; - u8 GetFacelineType() const; - u8 GetFacelineColor() const; - u8 GetFacelineWrinkle() const; - u8 GetFacelineMake() const; - u8 GetHairType() const; - u8 GetHairColor() const; - u8 GetHairFlip() const; - u8 GetEyeType() const; - u8 GetEyeColor() 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; - u8 GetEyebrowType() const; - u8 GetEyebrowColor() const; + EyebrowType GetEyebrowType() const; + CommonColor GetEyebrowColor() const; u8 GetEyebrowScale() const; u8 GetEyebrowAspect() const; u8 GetEyebrowRotate() const; u8 GetEyebrowX() const; u8 GetEyebrowY() const; - u8 GetNoseType() const; + NoseType GetNoseType() const; u8 GetNoseScale() const; u8 GetNoseY() const; - u8 GetMouthType() const; - u8 GetMouthColor() const; + MouthType GetMouthType() const; + CommonColor GetMouthColor() const; u8 GetMouthScale() const; u8 GetMouthAspect() const; u8 GetMouthY() const; - u8 GetBeardColor() const; - u8 GetBeardType() const; - u8 GetMustacheType() const; + CommonColor GetBeardColor() const; + BeardType GetBeardType() const; + MustacheType GetMustacheType() const; u8 GetMustacheScale() const; u8 GetMustacheY() const; - u8 GetGlassType() const; - u8 GetGlassColor() const; + GlassType GetGlassType() const; + CommonColor GetGlassColor() const; u8 GetGlassScale() const; u8 GetGlassY() const; - u8 GetMoleType() const; + MoleType GetMoleType() const; u8 GetMoleScale() const; u8 GetMoleX() const; u8 GetMoleY() const; @@ -73,52 +73,52 @@ private: Common::UUID create_id; Nickname name; u16 null_terminator; - u8 font_region; - u8 favorite_color; - u8 gender; + FontRegion font_region; + FavoriteColor favorite_color; + Gender 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; + 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; - u8 eyebrow_type; - u8 eyebrow_color; + EyebrowType eyebrow_type; + CommonColor eyebrow_color; u8 eyebrow_scale; u8 eyebrow_aspect; u8 eyebrow_rotate; u8 eyebrow_x; u8 eyebrow_y; - u8 nose_type; + NoseType nose_type; u8 nose_scale; u8 nose_y; - u8 mouth_type; - u8 mouth_color; + MouthType mouth_type; + CommonColor mouth_color; u8 mouth_scale; u8 mouth_aspect; u8 mouth_y; - u8 beard_color; - u8 beard_type; - u8 mustache_type; + CommonColor beard_color; + BeardType beard_type; + MustacheType mustache_type; u8 mustache_scale; u8 mustache_y; - u8 glasses_type; - u8 glasses_color; - u8 glasses_scale; - u8 glasses_y; - u8 mole_type; + GlassType glass_type; + CommonColor glass_color; + u8 glass_scale; + u8 glass_y; + MoleType mole_type; u8 mole_scale; u8 mole_x; u8 mole_y; diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp index de82481b0f..659288b512 100644 --- a/src/core/hle/service/mii/types/core_data.cpp +++ b/src/core/hle/service/mii/types/core_data.cpp @@ -1,6 +1,7 @@ // 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" @@ -14,17 +15,9 @@ void CoreData::SetDefault() { void CoreData::BuildRandom(Age age, Gender gender, Race race) { if (gender == Gender::All) { - gender = MiiUtil::GetRandomValue(Gender::Maximum); + gender = MiiUtil::GetRandomValue(Gender::Max); } - data.gender.Assign(gender); - data.favorite_color.Assign(MiiUtil::GetRandomValue(11)); - data.region_move.Assign(0); - data.font_region.Assign(FontRegion::Standard); - data.type.Assign(0); - data.height.Assign(64); - data.build.Assign(64); - if (age == Age::All) { const auto random{MiiUtil::GetRandomValue(10)}; if (random >= 8) { @@ -47,6 +40,14 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) { } } + 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(3); @@ -85,10 +86,10 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) { data.hair_type.Assign( hair_type_info.values[MiiUtil::GetRandomValue(hair_type_info.values_count)]); - data.hair_color.Assign(RawData::GetHairColorFromVer3( + SetHairColor(RawData::GetHairColorFromVer3( hair_color_info .values[MiiUtil::GetRandomValue(hair_color_info.values_count)])); - data.hair_flip.Assign(MiiUtil::GetRandomValue(HairFlip::Maximum)); + SetHairFlip(MiiUtil::GetRandomValue(HairFlip::Max)); data.eye_type.Assign( eye_type_info.values[MiiUtil::GetRandomValue(eye_type_info.values_count)]); @@ -98,13 +99,13 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) { const auto eye_rotate_offset{32 - RawData::EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; const auto eye_rotate{32 - RawData::EyeRotateLookup[data.eye_type]}; - data.eye_color.Assign(RawData::GetEyeColorFromVer3( + SetEyeColor(RawData::GetEyeColorFromVer3( eye_color_info.values[MiiUtil::GetRandomValue(eye_color_info.values_count)])); - data.eye_scale.Assign(4); - data.eye_aspect.Assign(3); - data.eye_rotate.Assign(eye_rotate_offset - eye_rotate); - data.eye_x.Assign(2); - data.eye_y.Assign(axis_y + 12); + SetEyeScale(4); + SetEyeAspect(3); + SetEyeRotate(static_cast(eye_rotate_offset - eye_rotate)); + SetEyeX(2); + SetEyeY(static_cast(axis_y + 12)); data.eyebrow_type.Assign( eyebrow_type_info @@ -116,57 +117,53 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) { const auto eyebrow_rotate{ 32 - RawData::EyebrowRotateLookup[static_cast(data.eyebrow_type.Value())]}; - data.eyebrow_color.Assign(data.hair_color); - data.eyebrow_scale.Assign(4); - data.eyebrow_aspect.Assign(3); - data.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate); - data.eyebrow_x.Assign(2); - data.eyebrow_y.Assign(axis_y + eyebrow_y); - - const auto nose_scale{gender == Gender::Female ? 3 : 4}; + SetEyebrowColor(GetHairColor()); + SetEyebrowScale(4); + SetEyebrowAspect(3); + SetEyebrowRotate(static_cast(eyebrow_rotate_offset - eyebrow_rotate)); + SetEyebrowX(2); + SetEyebrowY(static_cast(axis_y + eyebrow_y)); data.nose_type.Assign( nose_type_info.values[MiiUtil::GetRandomValue(nose_type_info.values_count)]); - data.nose_scale.Assign(nose_scale); - data.nose_y.Assign(axis_y + 9); + SetNoseScale(gender == Gender::Female ? 3 : 4); + SetNoseY(static_cast(axis_y + 9)); const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue(4) : 0}; data.mouth_type.Assign( mouth_type_info.values[MiiUtil::GetRandomValue(mouth_type_info.values_count)]); - data.mouth_color.Assign(RawData::GetMouthColorFromVer3(mouth_color)); - data.mouth_scale.Assign(4); - data.mouth_aspect.Assign(3); - data.mouth_y.Assign(axis_y + 13); + SetMouthColor(RawData::GetMouthColorFromVer3(mouth_color)); + SetMouthScale(4); + SetMouthAspect(3); + SetMouthY(static_cast(axis_y + 13)); - data.beard_color.Assign(data.hair_color); - data.mustache_scale.Assign(4); + SetBeardColor(GetHairColor()); + SetMustacheScale(4); if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue(10) < 2) { - const auto mustache_and_beard_flag{ - MiiUtil::GetRandomValue(BeardAndMustacheFlag::All)}; + 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::Beard1, BeardType::Beard5); + beard_type = MiiUtil::GetRandomValue(BeardType::Min, BeardType::Max); } if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == BeardAndMustacheFlag::Mustache) { - mustache_type = MiiUtil::GetRandomValue(MustacheType::Mustache1, - MustacheType::Mustache5); + mustache_type = MiiUtil::GetRandomValue(MustacheType::Min, MustacheType::Max); } - data.mustache_type.Assign(mustache_type); - data.beard_type.Assign(beard_type); - data.mustache_y.Assign(10); + SetMustacheType(mustache_type); + SetBeardType(beard_type); + SetMustacheY(10); } else { - data.mustache_type.Assign(MustacheType::None); - data.beard_type.Assign(BeardType::None); - data.mustache_y.Assign(axis_y + 10); + SetMustacheType(MustacheType::None); + SetBeardType(BeardType::None); + SetMustacheY(static_cast(axis_y + 10)); } const auto glasses_type_start{MiiUtil::GetRandomValue(100)}; @@ -178,15 +175,14 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) { } } - data.glasses_type.Assign(glasses_type); - data.glasses_color.Assign(RawData::GetGlassColorFromVer3(0)); - data.glasses_scale.Assign(4); - data.glasses_y.Assign(axis_y + 10); + SetGlassType(static_cast(glasses_type)); + SetGlassColor(RawData::GetGlassColorFromVer3(0)); + SetGlassScale(4); - data.mole_type.Assign(0); - data.mole_scale.Assign(4); - data.mole_x.Assign(2); - data.mole_y.Assign(20); + SetMoleType(MoleType::None); + SetMoleScale(4); + SetMoleX(2); + SetMoleY(20); } u32 CoreData::IsValid() const { @@ -195,15 +191,15 @@ u32 CoreData::IsValid() const { } void CoreData::SetFontRegion(FontRegion value) { - data.font_region.Assign(value); + data.font_region.Assign(static_cast(value)); } -void CoreData::SetFavoriteColor(u8 value) { - data.favorite_color.Assign(value); +void CoreData::SetFavoriteColor(FavoriteColor value) { + data.favorite_color.Assign(static_cast(value)); } void CoreData::SetGender(Gender value) { - data.gender.Assign(value); + data.gender.Assign(static_cast(value)); } void CoreData::SetHeight(u8 value) { @@ -222,40 +218,40 @@ void CoreData::SetRegionMove(u8 value) { data.region_move.Assign(value); } -void CoreData::SetFacelineType(u8 value) { - data.faceline_type.Assign(value); +void CoreData::SetFacelineType(FacelineType value) { + data.faceline_type.Assign(static_cast(value)); } -void CoreData::SetFacelineColor(u8 value) { - data.faceline_color.Assign(value); +void CoreData::SetFacelineColor(FacelineColor value) { + data.faceline_color.Assign(static_cast(value)); } -void CoreData::SetFacelineWrinkle(u8 value) { - data.faceline_wrinkle.Assign(value); +void CoreData::SetFacelineWrinkle(FacelineWrinkle value) { + data.faceline_wrinkle.Assign(static_cast(value)); } -void CoreData::SetFacelineMake(u8 value) { - data.faceline_makeup.Assign(value); +void CoreData::SetFacelineMake(FacelineMake value) { + data.faceline_makeup.Assign(static_cast(value)); } -void CoreData::SetHairType(u8 value) { - data.hair_type.Assign(value); +void CoreData::SetHairType(HairType value) { + data.hair_type.Assign(static_cast(value)); } -void CoreData::SetHairColor(u8 value) { - data.hair_color.Assign(value); +void CoreData::SetHairColor(CommonColor value) { + data.hair_color.Assign(static_cast(value)); } void CoreData::SetHairFlip(HairFlip value) { - data.hair_flip.Assign(value); + data.hair_flip.Assign(static_cast(value)); } -void CoreData::SetEyeType(u8 value) { - data.eye_type.Assign(value); +void CoreData::SetEyeType(EyeType value) { + data.eye_type.Assign(static_cast(value)); } -void CoreData::SetEyeColor(u8 value) { - data.eye_color.Assign(value); +void CoreData::SetEyeColor(CommonColor value) { + data.eye_color.Assign(static_cast(value)); } void CoreData::SetEyeScale(u8 value) { @@ -278,12 +274,12 @@ void CoreData::SetEyeY(u8 value) { data.eye_y.Assign(value); } -void CoreData::SetEyebrowType(u8 value) { - data.eyebrow_type.Assign(value); +void CoreData::SetEyebrowType(EyebrowType value) { + data.eyebrow_type.Assign(static_cast(value)); } -void CoreData::SetEyebrowColor(u8 value) { - data.eyebrow_color.Assign(value); +void CoreData::SetEyebrowColor(CommonColor value) { + data.eyebrow_color.Assign(static_cast(value)); } void CoreData::SetEyebrowScale(u8 value) { @@ -306,8 +302,8 @@ void CoreData::SetEyebrowY(u8 value) { data.eyebrow_y.Assign(value); } -void CoreData::SetNoseType(u8 value) { - data.nose_type.Assign(value); +void CoreData::SetNoseType(NoseType value) { + data.nose_type.Assign(static_cast(value)); } void CoreData::SetNoseScale(u8 value) { @@ -322,8 +318,8 @@ void CoreData::SetMouthType(u8 value) { data.mouth_type.Assign(value); } -void CoreData::SetMouthColor(u8 value) { - data.mouth_color.Assign(value); +void CoreData::SetMouthColor(CommonColor value) { + data.mouth_color.Assign(static_cast(value)); } void CoreData::SetMouthScale(u8 value) { @@ -338,16 +334,16 @@ void CoreData::SetMouthY(u8 value) { data.mouth_y.Assign(value); } -void CoreData::SetBeardColor(u8 value) { - data.beard_color.Assign(value); +void CoreData::SetBeardColor(CommonColor value) { + data.beard_color.Assign(static_cast(value)); } void CoreData::SetBeardType(BeardType value) { - data.beard_type.Assign(value); + data.beard_type.Assign(static_cast(value)); } void CoreData::SetMustacheType(MustacheType value) { - data.mustache_type.Assign(value); + data.mustache_type.Assign(static_cast(value)); } void CoreData::SetMustacheScale(u8 value) { @@ -358,12 +354,12 @@ void CoreData::SetMustacheY(u8 value) { data.mustache_y.Assign(value); } -void CoreData::SetGlassType(u8 value) { - data.glasses_type.Assign(value); +void CoreData::SetGlassType(GlassType value) { + data.glasses_type.Assign(static_cast(value)); } -void CoreData::SetGlassColor(u8 value) { - data.glasses_color.Assign(value); +void CoreData::SetGlassColor(CommonColor value) { + data.glasses_color.Assign(static_cast(value)); } void CoreData::SetGlassScale(u8 value) { @@ -374,8 +370,8 @@ void CoreData::SetGlassY(u8 value) { data.glasses_y.Assign(value); } -void CoreData::SetMoleType(u8 value) { - data.mole_type.Assign(value); +void CoreData::SetMoleType(MoleType value) { + data.mole_type.Assign(static_cast(value)); } void CoreData::SetMoleScale(u8 value) { @@ -394,16 +390,16 @@ void CoreData::SetNickname(Nickname nickname) { name = nickname; } -u8 CoreData::GetFontRegion() const { - return static_cast(data.font_region.Value()); +FontRegion CoreData::GetFontRegion() const { + return static_cast(data.font_region.Value()); } -u8 CoreData::GetFavoriteColor() const { - return static_cast(data.favorite_color.Value()); +FavoriteColor CoreData::GetFavoriteColor() const { + return static_cast(data.favorite_color.Value()); } -u8 CoreData::GetGender() const { - return static_cast(data.gender.Value()); +Gender CoreData::GetGender() const { + return static_cast(data.gender.Value()); } u8 CoreData::GetHeight() const { @@ -422,40 +418,40 @@ u8 CoreData::GetRegionMove() const { return static_cast(data.region_move.Value()); } -u8 CoreData::GetFacelineType() const { - return static_cast(data.faceline_type.Value()); +FacelineType CoreData::GetFacelineType() const { + return static_cast(data.faceline_type.Value()); } -u8 CoreData::GetFacelineColor() const { - return static_cast(data.faceline_color.Value()); +FacelineColor CoreData::GetFacelineColor() const { + return static_cast(data.faceline_color.Value()); } -u8 CoreData::GetFacelineWrinkle() const { - return static_cast(data.faceline_wrinkle.Value()); +FacelineWrinkle CoreData::GetFacelineWrinkle() const { + return static_cast(data.faceline_wrinkle.Value()); } -u8 CoreData::GetFacelineMake() const { - return static_cast(data.faceline_makeup.Value()); +FacelineMake CoreData::GetFacelineMake() const { + return static_cast(data.faceline_makeup.Value()); } -u8 CoreData::GetHairType() const { - return static_cast(data.hair_type.Value()); +HairType CoreData::GetHairType() const { + return static_cast(data.hair_type.Value()); } -u8 CoreData::GetHairColor() const { - return static_cast(data.hair_color.Value()); +CommonColor CoreData::GetHairColor() const { + return static_cast(data.hair_color.Value()); } -u8 CoreData::GetHairFlip() const { - return static_cast(data.hair_flip.Value()); +HairFlip CoreData::GetHairFlip() const { + return static_cast(data.hair_flip.Value()); } -u8 CoreData::GetEyeType() const { - return static_cast(data.eye_type.Value()); +EyeType CoreData::GetEyeType() const { + return static_cast(data.eye_type.Value()); } -u8 CoreData::GetEyeColor() const { - return static_cast(data.eye_color.Value()); +CommonColor CoreData::GetEyeColor() const { + return static_cast(data.eye_color.Value()); } u8 CoreData::GetEyeScale() const { @@ -478,12 +474,12 @@ u8 CoreData::GetEyeY() const { return static_cast(data.eye_y.Value()); } -u8 CoreData::GetEyebrowType() const { - return static_cast(data.eyebrow_type.Value()); +EyebrowType CoreData::GetEyebrowType() const { + return static_cast(data.eyebrow_type.Value()); } -u8 CoreData::GetEyebrowColor() const { - return static_cast(data.eyebrow_color.Value()); +CommonColor CoreData::GetEyebrowColor() const { + return static_cast(data.eyebrow_color.Value()); } u8 CoreData::GetEyebrowScale() const { @@ -506,8 +502,8 @@ u8 CoreData::GetEyebrowY() const { return static_cast(data.eyebrow_y.Value()); } -u8 CoreData::GetNoseType() const { - return static_cast(data.nose_type.Value()); +NoseType CoreData::GetNoseType() const { + return static_cast(data.nose_type.Value()); } u8 CoreData::GetNoseScale() const { @@ -518,12 +514,12 @@ u8 CoreData::GetNoseY() const { return static_cast(data.nose_y.Value()); } -u8 CoreData::GetMouthType() const { - return static_cast(data.mouth_type.Value()); +MouthType CoreData::GetMouthType() const { + return static_cast(data.mouth_type.Value()); } -u8 CoreData::GetMouthColor() const { - return static_cast(data.mouth_color.Value()); +CommonColor CoreData::GetMouthColor() const { + return static_cast(data.mouth_color.Value()); } u8 CoreData::GetMouthScale() const { @@ -538,16 +534,16 @@ u8 CoreData::GetMouthY() const { return static_cast(data.mouth_y.Value()); } -u8 CoreData::GetBeardColor() const { - return static_cast(data.beard_color.Value()); +CommonColor CoreData::GetBeardColor() const { + return static_cast(data.beard_color.Value()); } -u8 CoreData::GetBeardType() const { - return static_cast(data.beard_type.Value()); +BeardType CoreData::GetBeardType() const { + return static_cast(data.beard_type.Value()); } -u8 CoreData::GetMustacheType() const { - return static_cast(data.mustache_type.Value()); +MustacheType CoreData::GetMustacheType() const { + return static_cast(data.mustache_type.Value()); } u8 CoreData::GetMustacheScale() const { @@ -558,12 +554,12 @@ u8 CoreData::GetMustacheY() const { return static_cast(data.mustache_y.Value()); } -u8 CoreData::GetGlassType() const { - return static_cast(data.glasses_type.Value()); +GlassType CoreData::GetGlassType() const { + return static_cast(data.glasses_type.Value()); } -u8 CoreData::GetGlassColor() const { - return static_cast(data.glasses_color.Value()); +CommonColor CoreData::GetGlassColor() const { + return static_cast(data.glasses_color.Value()); } u8 CoreData::GetGlassScale() const { @@ -574,8 +570,8 @@ u8 CoreData::GetGlassY() const { return static_cast(data.glasses_y.Value()); } -u8 CoreData::GetMoleType() const { - return static_cast(data.mole_type.Value()); +MoleType CoreData::GetMoleType() const { + return static_cast(data.mole_type.Value()); } u8 CoreData::GetMoleScale() const { @@ -599,7 +595,7 @@ Nickname CoreData::GetDefaultNickname() const { } Nickname CoreData::GetInvalidNickname() const { - return {u'?', u'?', u' ', u'?'}; + 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 index 411c123b3f..cebcd2ee44 100644 --- a/src/core/hle/service/mii/types/core_data.h +++ b/src/core/hle/service/mii/types/core_data.h @@ -15,7 +15,7 @@ struct StoreDataBitFields { BitField<8, 7, u32> height; BitField<15, 1, u32> mole_type; BitField<16, 7, u32> build; - BitField<23, 1, HairFlip> hair_flip; + BitField<23, 1, u32> hair_flip; BitField<24, 7, u32> hair_color; BitField<31, 1, u32> type; }; @@ -24,7 +24,7 @@ struct StoreDataBitFields { u32 word_1{}; BitField<0, 7, u32> eye_color; - BitField<7, 1, Gender> gender; + BitField<7, 1, u32> gender; BitField<8, 7, u32> eyebrow_color; BitField<16, 7, u32> mouth_color; BitField<24, 7, u32> beard_color; @@ -37,7 +37,7 @@ struct StoreDataBitFields { 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<22, 2, u32> font_region; BitField<24, 5, u32> eye_y; BitField<29, 3, u32> glasses_scale; }; @@ -46,9 +46,9 @@ struct StoreDataBitFields { u32 word_3{}; BitField<0, 5, u32> eyebrow_type; - BitField<5, 3, MustacheType> mustache_type; + BitField<5, 3, u32> mustache_type; BitField<8, 5, u32> nose_type; - BitField<13, 3, BeardType> beard_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; @@ -104,102 +104,102 @@ public: u32 IsValid() const; void SetFontRegion(FontRegion value); - void SetFavoriteColor(u8 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(u8 value); - void SetFacelineColor(u8 value); - void SetFacelineWrinkle(u8 value); - void SetFacelineMake(u8 value); - void SetHairType(u8 value); - void SetHairColor(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(u8 value); - void SetEyeColor(u8 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(u8 value); - void SetEyebrowColor(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(u8 value); + void SetNoseType(NoseType value); void SetNoseScale(u8 value); void SetNoseY(u8 value); void SetMouthType(u8 value); - void SetMouthColor(u8 value); + void SetMouthColor(CommonColor value); void SetMouthScale(u8 value); void SetMouthAspect(u8 value); void SetMouthY(u8 value); - void SetBeardColor(u8 value); + void SetBeardColor(CommonColor value); void SetBeardType(BeardType value); void SetMustacheType(MustacheType value); void SetMustacheScale(u8 value); void SetMustacheY(u8 value); - void SetGlassType(u8 value); - void SetGlassColor(u8 value); + void SetGlassType(GlassType value); + void SetGlassColor(CommonColor value); void SetGlassScale(u8 value); void SetGlassY(u8 value); - void SetMoleType(u8 value); + void SetMoleType(MoleType value); void SetMoleScale(u8 value); void SetMoleX(u8 value); void SetMoleY(u8 value); void SetNickname(Nickname nickname); - u8 GetFontRegion() const; - u8 GetFavoriteColor() const; - u8 GetGender() const; + FontRegion GetFontRegion() const; + FavoriteColor GetFavoriteColor() const; + Gender GetGender() const; u8 GetHeight() const; u8 GetBuild() const; u8 GetType() const; u8 GetRegionMove() const; - u8 GetFacelineType() const; - u8 GetFacelineColor() const; - u8 GetFacelineWrinkle() const; - u8 GetFacelineMake() const; - u8 GetHairType() const; - u8 GetHairColor() const; - u8 GetHairFlip() const; - u8 GetEyeType() const; - u8 GetEyeColor() 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; - u8 GetEyebrowType() const; - u8 GetEyebrowColor() const; + EyebrowType GetEyebrowType() const; + CommonColor GetEyebrowColor() const; u8 GetEyebrowScale() const; u8 GetEyebrowAspect() const; u8 GetEyebrowRotate() const; u8 GetEyebrowX() const; u8 GetEyebrowY() const; - u8 GetNoseType() const; + NoseType GetNoseType() const; u8 GetNoseScale() const; u8 GetNoseY() const; - u8 GetMouthType() const; - u8 GetMouthColor() const; + MouthType GetMouthType() const; + CommonColor GetMouthColor() const; u8 GetMouthScale() const; u8 GetMouthAspect() const; u8 GetMouthY() const; - u8 GetBeardColor() const; - u8 GetBeardType() const; - u8 GetMustacheType() const; + CommonColor GetBeardColor() const; + BeardType GetBeardType() const; + MustacheType GetMustacheType() const; u8 GetMustacheScale() const; u8 GetMustacheY() const; - u8 GetGlassType() const; - u8 GetGlassColor() const; + GlassType GetGlassType() const; + CommonColor GetGlassColor() const; u8 GetGlassScale() const; u8 GetGlassY() const; - u8 GetMoleType() const; + MoleType GetMoleType() const; u8 GetMoleScale() const; u8 GetMoleX() const; u8 GetMoleY() const; @@ -207,6 +207,7 @@ public: Nickname GetDefaultNickname() const; Nickname GetInvalidNickname() const; +private: StoreDataBitFields data{}; Nickname name{}; }; diff --git a/src/core/hle/service/mii/types/raw_data.cpp b/src/core/hle/service/mii/types/raw_data.cpp index ed299521f8..5143abcc89 100644 --- a/src/core/hle/service/mii/types/raw_data.cpp +++ b/src/core/hle/service/mii/types/raw_data.cpp @@ -5,11 +5,11 @@ namespace Service::Mii::RawData { -constexpr std::array FromVer3FacelineColorTable{ +constexpr std::array(FacelineColor::Count)> FromVer3FacelineColorTable{ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, }; -constexpr std::array FromVer3HairColorTable{ +constexpr std::array(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, @@ -18,7 +18,7 @@ constexpr std::array FromVer3HairColorTable{ 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4, }; -constexpr std::array FromVer3EyeColorTable{ +constexpr std::array(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, @@ -27,7 +27,7 @@ constexpr std::array FromVer3EyeColorTable{ 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, }; -constexpr std::array FromVer3MouthlineColorTable{ +constexpr std::array(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, @@ -36,7 +36,7 @@ constexpr std::array FromVer3MouthlineColorTable{ 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3, }; -constexpr std::array FromVer3GlassColorTable{ +constexpr std::array(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, @@ -45,7 +45,7 @@ constexpr std::array FromVer3GlassColorTable{ 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, }; -constexpr std::array FromVer3GlassTypeTable{ +constexpr std::array(GlassType::Count)> FromVer3GlassTypeTable{ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7, }; @@ -90,7 +90,7 @@ const std::array BaseMii{ .face_makeup = 0, .hair_type = 33, .hair_color = 1, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 2, .eye_color = 0, .eye_scale = 4, @@ -113,8 +113,8 @@ const std::array BaseMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -128,10 +128,10 @@ const std::array BaseMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 0, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -142,7 +142,7 @@ const std::array BaseMii{ .face_makeup = 0, .hair_type = 12, .hair_color = 1, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 4, .eye_color = 0, .eye_scale = 4, @@ -165,8 +165,8 @@ const std::array BaseMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -180,10 +180,10 @@ const std::array BaseMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 0, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -197,7 +197,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 68, .hair_color = 0, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 2, .eye_color = 0, .eye_scale = 4, @@ -220,8 +220,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -235,10 +235,10 @@ const std::array DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 4, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -249,7 +249,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 55, .hair_color = 6, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 2, .eye_color = 4, .eye_scale = 4, @@ -272,8 +272,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -287,10 +287,10 @@ const std::array DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 5, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -301,7 +301,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 33, .hair_color = 1, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 2, .eye_color = 0, .eye_scale = 4, @@ -324,8 +324,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -339,10 +339,10 @@ const std::array DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 0, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -353,7 +353,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 24, .hair_color = 0, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 4, .eye_color = 0, .eye_scale = 4, @@ -376,8 +376,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -391,10 +391,10 @@ const std::array DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 2, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -405,7 +405,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 14, .hair_color = 7, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 4, .eye_color = 5, .eye_scale = 4, @@ -428,8 +428,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -443,10 +443,10 @@ const std::array DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 6, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -457,7 +457,7 @@ const std::array DefaultMii{ .face_makeup = 0, .hair_type = 12, .hair_color = 1, - .hair_flip = HairFlip::Left, + .hair_flip = 0, .eye_type = 4, .eye_color = 0, .eye_scale = 4, @@ -480,8 +480,8 @@ const std::array DefaultMii{ .mouth_scale = 4, .mouth_aspect = 3, .mouth_y = 13, - .mustache_type = MustacheType::None, - .beard_type = BeardType::None, + .mustache_type = 0, + .beard_type = 0, .beard_color = 0, .mustache_scale = 4, .mustache_y = 10, @@ -495,10 +495,10 @@ const std::array DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 7, .region_move = 0, - .font_region = FontRegion::Standard, + .font_region = 0, .type = 0, .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, @@ -507,128 +507,128 @@ const std::array DefaultMii{ const std::array RandomMiiFaceline{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::White), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(Race::White), .values_count = 13, .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(Race::White), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::Asian), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Asian), .values_count = 13, .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(Race::Asian), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::White), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::White), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::White), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::Asian), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Asian), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::Asian), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, @@ -675,128 +675,128 @@ const std::array RandomMiiFacelineColor{ const std::array RandomMiiFacelineWrinkle{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, @@ -804,128 +804,128 @@ const std::array RandomMiiFacelineWrinkle{ const std::array RandomMiiFacelineMakeup{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, @@ -933,147 +933,147 @@ const std::array RandomMiiFacelineMakeup{ const std::array RandomMiiHairType{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::Asian), .values_count = 18, .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, @@ -1139,148 +1139,148 @@ const std::array RandomMiiHairColor{ const std::array RandomMiiEyeType{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, @@ -1307,131 +1307,131 @@ const std::array RandomMiiEyeColor{ const std::array RandomMiiEyebrowType{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::Black), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Black), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(Race::Black), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::Black), .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Black), .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::Black), .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::White), .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::White), .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::White), .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::Asian), .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Asian), .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::Asian), .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, @@ -1439,128 +1439,128 @@ const std::array RandomMiiEyebrowType{ const std::array RandomMiiNoseType{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::Black), .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Black), .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(Race::Black), .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::White), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(Race::White), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(Race::White), .values_count = 15, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(Race::Asian), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Asian), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(Race::Asian), .values_count = 15, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::Black), .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Black), .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::Black), .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::White), .values_count = 12, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::White), .values_count = 11, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::White), .values_count = 10, .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(Race::Asian), .values_count = 12, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(Race::Asian), .values_count = 11, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(Race::Asian), .values_count = 10, .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, }, @@ -1568,145 +1568,145 @@ const std::array RandomMiiNoseType{ const std::array RandomMiiMouthType{ RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Male), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Young), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Normal), + .race = static_cast(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}, }, RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + .gender = static_cast(Gender::Female), + .age = static_cast(Age::Old), + .race = static_cast(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}, @@ -1755,24 +1755,24 @@ u8 FromVer3GetGlassType(u8 type) { return FromVer3GlassTypeTable[type]; } -u8 GetFacelineColorFromVer3(u8 color) { - return Ver3FacelineColorTable[color]; +FacelineColor GetFacelineColorFromVer3(u32 color) { + return static_cast(Ver3FacelineColorTable[color]); } -u8 GetHairColorFromVer3(u32 color) { - return Ver3HairColorTable[color]; +CommonColor GetHairColorFromVer3(u32 color) { + return static_cast(Ver3HairColorTable[color]); } -u8 GetEyeColorFromVer3(u32 color) { - return Ver3EyeColorTable[color]; +CommonColor GetEyeColorFromVer3(u32 color) { + return static_cast(Ver3EyeColorTable[color]); } -u8 GetMouthColorFromVer3(u32 color) { - return Ver3MouthColorTable[color]; +CommonColor GetMouthColorFromVer3(u32 color) { + return static_cast(Ver3MouthColorTable[color]); } -u8 GetGlassColorFromVer3(u8 color) { - return Ver3GlassColorTable[color]; +CommonColor GetGlassColorFromVer3(u32 color) { + return static_cast(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 index 8c910096f7..9a4cfa738e 100644 --- a/src/core/hle/service/mii/types/raw_data.h +++ b/src/core/hle/service/mii/types/raw_data.h @@ -15,9 +15,9 @@ struct RandomMiiValues { static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); struct RandomMiiData4 { - Gender gender{}; - Age age{}; - Race race{}; + u32 gender{}; + u32 age{}; + u32 race{}; u32 values_count{}; std::array values{}; }; @@ -64,10 +64,10 @@ u8 FromVer3GetMouthlineColor(u8 color); u8 FromVer3GetGlassColor(u8 color); u8 FromVer3GetGlassType(u8 type); -u8 GetFacelineColorFromVer3(u8 color); -u8 GetHairColorFromVer3(u32 color); -u8 GetEyeColorFromVer3(u32 color); -u8 GetMouthColorFromVer3(u32 color); -u8 GetGlassColorFromVer3(u8 color); +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 index 91dfd3271c..8fce636c7b 100644 --- a/src/core/hle/service/mii/types/store_data.cpp +++ b/src/core/hle/service/mii/types/store_data.cpp @@ -11,16 +11,16 @@ void StoreData::BuildDefault(u32 mii_index) { const auto& default_mii = RawData::DefaultMii[mii_index]; core_data.SetDefault(); - core_data.SetFacelineType(static_cast(default_mii.face_type)); + core_data.SetFacelineType(static_cast(default_mii.face_type)); core_data.SetFacelineColor( RawData::GetFacelineColorFromVer3(static_cast(default_mii.face_color))); - core_data.SetFacelineWrinkle(static_cast(default_mii.face_wrinkle)); - core_data.SetFacelineMake(static_cast(default_mii.face_makeup)); + core_data.SetFacelineWrinkle(static_cast(default_mii.face_wrinkle)); + core_data.SetFacelineMake(static_cast(default_mii.face_makeup)); - core_data.SetHairType(static_cast(default_mii.hair_type)); + core_data.SetHairType(static_cast(default_mii.hair_type)); core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast(default_mii.hair_color))); - core_data.SetHairFlip(default_mii.hair_flip); - core_data.SetEyeType(static_cast(default_mii.eye_type)); + core_data.SetHairFlip(static_cast(default_mii.hair_flip)); + core_data.SetEyeType(static_cast(default_mii.eye_type)); core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast(default_mii.eye_color))); core_data.SetEyeScale(static_cast(default_mii.eye_scale)); core_data.SetEyeAspect(static_cast(default_mii.eye_aspect)); @@ -28,7 +28,7 @@ void StoreData::BuildDefault(u32 mii_index) { core_data.SetEyeX(static_cast(default_mii.eye_x)); core_data.SetEyeY(static_cast(default_mii.eye_y)); - core_data.SetEyebrowType(static_cast(default_mii.eyebrow_type)); + core_data.SetEyebrowType(static_cast(default_mii.eyebrow_type)); core_data.SetEyebrowColor( RawData::GetHairColorFromVer3(static_cast(default_mii.eyebrow_color))); core_data.SetEyebrowScale(static_cast(default_mii.eyebrow_scale)); @@ -37,7 +37,7 @@ void StoreData::BuildDefault(u32 mii_index) { core_data.SetEyebrowX(static_cast(default_mii.eyebrow_x)); core_data.SetEyebrowY(static_cast(default_mii.eyebrow_y)); - core_data.SetNoseType(static_cast(default_mii.nose_type)); + core_data.SetNoseType(static_cast(default_mii.nose_type)); core_data.SetNoseScale(static_cast(default_mii.nose_scale)); core_data.SetNoseY(static_cast(default_mii.nose_y)); @@ -48,30 +48,30 @@ void StoreData::BuildDefault(u32 mii_index) { core_data.SetMouthAspect(static_cast(default_mii.mouth_aspect)); core_data.SetMouthY(static_cast(default_mii.mouth_y)); - core_data.SetMustacheType(default_mii.mustache_type); - core_data.SetBeardType(default_mii.beard_type); + core_data.SetMustacheType(static_cast(default_mii.mustache_type)); + core_data.SetBeardType(static_cast(default_mii.beard_type)); core_data.SetBeardColor( RawData::GetHairColorFromVer3(static_cast(default_mii.beard_color))); core_data.SetMustacheScale(static_cast(default_mii.mustache_scale)); core_data.SetMustacheY(static_cast(default_mii.mustache_y)); - core_data.SetGlassType(static_cast(default_mii.glasses_type)); + core_data.SetGlassType(static_cast(default_mii.glasses_type)); core_data.SetGlassColor( RawData::GetGlassColorFromVer3(static_cast(default_mii.glasses_color))); core_data.SetGlassScale(static_cast(default_mii.glasses_scale)); core_data.SetGlassY(static_cast(default_mii.glasses_y)); - core_data.SetMoleType(static_cast(default_mii.mole_type)); + core_data.SetMoleType(static_cast(default_mii.mole_type)); core_data.SetMoleScale(static_cast(default_mii.mole_scale)); core_data.SetMoleX(static_cast(default_mii.mole_x)); core_data.SetMoleY(static_cast(default_mii.mole_y)); core_data.SetHeight(static_cast(default_mii.height)); core_data.SetBuild(static_cast(default_mii.weight)); - core_data.SetGender(default_mii.gender); - core_data.SetFavoriteColor(static_cast(default_mii.favorite_color)); + core_data.SetGender(static_cast(default_mii.gender)); + core_data.SetFavoriteColor(static_cast(default_mii.favorite_color)); core_data.SetRegionMove(static_cast(default_mii.region_move)); - core_data.SetFontRegion(default_mii.font_region); + core_data.SetFontRegion(static_cast(default_mii.font_region)); core_data.SetType(static_cast(default_mii.type)); core_data.SetNickname(default_mii.nickname); @@ -85,16 +85,16 @@ void StoreData::BuildBase(Gender gender) { const auto& default_mii = RawData::BaseMii[gender == Gender::Female ? 1 : 0]; core_data.SetDefault(); - core_data.SetFacelineType(static_cast(default_mii.face_type)); + core_data.SetFacelineType(static_cast(default_mii.face_type)); core_data.SetFacelineColor( RawData::GetFacelineColorFromVer3(static_cast(default_mii.face_color))); - core_data.SetFacelineWrinkle(static_cast(default_mii.face_wrinkle)); - core_data.SetFacelineMake(static_cast(default_mii.face_makeup)); + core_data.SetFacelineWrinkle(static_cast(default_mii.face_wrinkle)); + core_data.SetFacelineMake(static_cast(default_mii.face_makeup)); - core_data.SetHairType(static_cast(default_mii.hair_type)); + core_data.SetHairType(static_cast(default_mii.hair_type)); core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast(default_mii.hair_color))); - core_data.SetHairFlip(default_mii.hair_flip); - core_data.SetEyeType(static_cast(default_mii.eye_type)); + core_data.SetHairFlip(static_cast(default_mii.hair_flip)); + core_data.SetEyeType(static_cast(default_mii.eye_type)); core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast(default_mii.eye_color))); core_data.SetEyeScale(static_cast(default_mii.eye_scale)); core_data.SetEyeAspect(static_cast(default_mii.eye_aspect)); @@ -102,7 +102,7 @@ void StoreData::BuildBase(Gender gender) { core_data.SetEyeX(static_cast(default_mii.eye_x)); core_data.SetEyeY(static_cast(default_mii.eye_y)); - core_data.SetEyebrowType(static_cast(default_mii.eyebrow_type)); + core_data.SetEyebrowType(static_cast(default_mii.eyebrow_type)); core_data.SetEyebrowColor( RawData::GetHairColorFromVer3(static_cast(default_mii.eyebrow_color))); core_data.SetEyebrowScale(static_cast(default_mii.eyebrow_scale)); @@ -111,7 +111,7 @@ void StoreData::BuildBase(Gender gender) { core_data.SetEyebrowX(static_cast(default_mii.eyebrow_x)); core_data.SetEyebrowY(static_cast(default_mii.eyebrow_y)); - core_data.SetNoseType(static_cast(default_mii.nose_type)); + core_data.SetNoseType(static_cast(default_mii.nose_type)); core_data.SetNoseScale(static_cast(default_mii.nose_scale)); core_data.SetNoseY(static_cast(default_mii.nose_y)); @@ -122,30 +122,30 @@ void StoreData::BuildBase(Gender gender) { core_data.SetMouthAspect(static_cast(default_mii.mouth_aspect)); core_data.SetMouthY(static_cast(default_mii.mouth_y)); - core_data.SetMustacheType(default_mii.mustache_type); - core_data.SetBeardType(default_mii.beard_type); + core_data.SetMustacheType(static_cast(default_mii.mustache_type)); + core_data.SetBeardType(static_cast(default_mii.beard_type)); core_data.SetBeardColor( RawData::GetHairColorFromVer3(static_cast(default_mii.beard_color))); core_data.SetMustacheScale(static_cast(default_mii.mustache_scale)); core_data.SetMustacheY(static_cast(default_mii.mustache_y)); - core_data.SetGlassType(static_cast(default_mii.glasses_type)); + core_data.SetGlassType(static_cast(default_mii.glasses_type)); core_data.SetGlassColor( RawData::GetGlassColorFromVer3(static_cast(default_mii.glasses_color))); core_data.SetGlassScale(static_cast(default_mii.glasses_scale)); core_data.SetGlassY(static_cast(default_mii.glasses_y)); - core_data.SetMoleType(static_cast(default_mii.mole_type)); + core_data.SetMoleType(static_cast(default_mii.mole_type)); core_data.SetMoleScale(static_cast(default_mii.mole_scale)); core_data.SetMoleX(static_cast(default_mii.mole_x)); core_data.SetMoleY(static_cast(default_mii.mole_y)); core_data.SetHeight(static_cast(default_mii.height)); core_data.SetBuild(static_cast(default_mii.weight)); - core_data.SetGender(default_mii.gender); - core_data.SetFavoriteColor(static_cast(default_mii.favorite_color)); + core_data.SetGender(static_cast(default_mii.gender)); + core_data.SetFavoriteColor(static_cast(default_mii.favorite_color)); core_data.SetRegionMove(static_cast(default_mii.region_move)); - core_data.SetFontRegion(default_mii.font_region); + core_data.SetFontRegion(static_cast(default_mii.font_region)); core_data.SetType(static_cast(default_mii.type)); core_data.SetNickname(default_mii.nickname); @@ -184,7 +184,7 @@ void StoreData::SetFontRegion(FontRegion value) { core_data.SetFontRegion(value); } -void StoreData::SetFavoriteColor(u8 value) { +void StoreData::SetFavoriteColor(FavoriteColor value) { core_data.SetFavoriteColor(value); } @@ -208,27 +208,27 @@ void StoreData::SetRegionMove(u8 value) { core_data.SetRegionMove(value); } -void StoreData::SetFacelineType(u8 value) { +void StoreData::SetFacelineType(FacelineType value) { core_data.SetFacelineType(value); } -void StoreData::SetFacelineColor(u8 value) { +void StoreData::SetFacelineColor(FacelineColor value) { core_data.SetFacelineColor(value); } -void StoreData::SetFacelineWrinkle(u8 value) { +void StoreData::SetFacelineWrinkle(FacelineWrinkle value) { core_data.SetFacelineWrinkle(value); } -void StoreData::SetFacelineMake(u8 value) { +void StoreData::SetFacelineMake(FacelineMake value) { core_data.SetFacelineMake(value); } -void StoreData::SetHairType(u8 value) { +void StoreData::SetHairType(HairType value) { core_data.SetHairType(value); } -void StoreData::SetHairColor(u8 value) { +void StoreData::SetHairColor(CommonColor value) { core_data.SetHairColor(value); } @@ -236,11 +236,11 @@ void StoreData::SetHairFlip(HairFlip value) { core_data.SetHairFlip(value); } -void StoreData::SetEyeType(u8 value) { +void StoreData::SetEyeType(EyeType value) { core_data.SetEyeType(value); } -void StoreData::SetEyeColor(u8 value) { +void StoreData::SetEyeColor(CommonColor value) { core_data.SetEyeColor(value); } @@ -264,11 +264,11 @@ void StoreData::SetEyeY(u8 value) { core_data.SetEyeY(value); } -void StoreData::SetEyebrowType(u8 value) { +void StoreData::SetEyebrowType(EyebrowType value) { core_data.SetEyebrowType(value); } -void StoreData::SetEyebrowColor(u8 value) { +void StoreData::SetEyebrowColor(CommonColor value) { core_data.SetEyebrowColor(value); } @@ -292,7 +292,7 @@ void StoreData::SetEyebrowY(u8 value) { core_data.SetEyebrowY(value); } -void StoreData::SetNoseType(u8 value) { +void StoreData::SetNoseType(NoseType value) { core_data.SetNoseType(value); } @@ -308,7 +308,7 @@ void StoreData::SetMouthType(u8 value) { core_data.SetMouthType(value); } -void StoreData::SetMouthColor(u8 value) { +void StoreData::SetMouthColor(CommonColor value) { core_data.SetMouthColor(value); } @@ -324,7 +324,7 @@ void StoreData::SetMouthY(u8 value) { core_data.SetMouthY(value); } -void StoreData::SetBeardColor(u8 value) { +void StoreData::SetBeardColor(CommonColor value) { core_data.SetBeardColor(value); } @@ -344,11 +344,11 @@ void StoreData::SetMustacheY(u8 value) { core_data.SetMustacheY(value); } -void StoreData::SetGlassType(u8 value) { +void StoreData::SetGlassType(GlassType value) { core_data.SetGlassType(value); } -void StoreData::SetGlassColor(u8 value) { +void StoreData::SetGlassColor(CommonColor value) { core_data.SetGlassColor(value); } @@ -360,7 +360,7 @@ void StoreData::SetGlassY(u8 value) { core_data.SetGlassY(value); } -void StoreData::SetMoleType(u8 value) { +void StoreData::SetMoleType(MoleType value) { core_data.SetMoleType(value); } @@ -388,11 +388,11 @@ FontRegion StoreData::GetFontRegion() const { return static_cast(core_data.GetFontRegion()); } -u8 StoreData::GetFavoriteColor() const { +FavoriteColor StoreData::GetFavoriteColor() const { return core_data.GetFavoriteColor(); } -u8 StoreData::GetGender() const { +Gender StoreData::GetGender() const { return core_data.GetGender(); } @@ -412,39 +412,39 @@ u8 StoreData::GetRegionMove() const { return core_data.GetRegionMove(); } -u8 StoreData::GetFacelineType() const { +FacelineType StoreData::GetFacelineType() const { return core_data.GetFacelineType(); } -u8 StoreData::GetFacelineColor() const { +FacelineColor StoreData::GetFacelineColor() const { return core_data.GetFacelineColor(); } -u8 StoreData::GetFacelineWrinkle() const { +FacelineWrinkle StoreData::GetFacelineWrinkle() const { return core_data.GetFacelineWrinkle(); } -u8 StoreData::GetFacelineMake() const { +FacelineMake StoreData::GetFacelineMake() const { return core_data.GetFacelineMake(); } -u8 StoreData::GetHairType() const { +HairType StoreData::GetHairType() const { return core_data.GetHairType(); } -u8 StoreData::GetHairColor() const { +CommonColor StoreData::GetHairColor() const { return core_data.GetHairColor(); } -u8 StoreData::GetHairFlip() const { +HairFlip StoreData::GetHairFlip() const { return core_data.GetHairFlip(); } -u8 StoreData::GetEyeType() const { +EyeType StoreData::GetEyeType() const { return core_data.GetEyeType(); } -u8 StoreData::GetEyeColor() const { +CommonColor StoreData::GetEyeColor() const { return core_data.GetEyeColor(); } @@ -468,11 +468,11 @@ u8 StoreData::GetEyeY() const { return core_data.GetEyeY(); } -u8 StoreData::GetEyebrowType() const { +EyebrowType StoreData::GetEyebrowType() const { return core_data.GetEyebrowType(); } -u8 StoreData::GetEyebrowColor() const { +CommonColor StoreData::GetEyebrowColor() const { return core_data.GetEyebrowColor(); } @@ -496,7 +496,7 @@ u8 StoreData::GetEyebrowY() const { return core_data.GetEyebrowY(); } -u8 StoreData::GetNoseType() const { +NoseType StoreData::GetNoseType() const { return core_data.GetNoseType(); } @@ -508,11 +508,11 @@ u8 StoreData::GetNoseY() const { return core_data.GetNoseY(); } -u8 StoreData::GetMouthType() const { +MouthType StoreData::GetMouthType() const { return core_data.GetMouthType(); } -u8 StoreData::GetMouthColor() const { +CommonColor StoreData::GetMouthColor() const { return core_data.GetMouthColor(); } @@ -528,15 +528,15 @@ u8 StoreData::GetMouthY() const { return core_data.GetMouthY(); } -u8 StoreData::GetBeardColor() const { +CommonColor StoreData::GetBeardColor() const { return core_data.GetBeardColor(); } -u8 StoreData::GetBeardType() const { +BeardType StoreData::GetBeardType() const { return core_data.GetBeardType(); } -u8 StoreData::GetMustacheType() const { +MustacheType StoreData::GetMustacheType() const { return core_data.GetMustacheType(); } @@ -548,11 +548,11 @@ u8 StoreData::GetMustacheY() const { return core_data.GetMustacheY(); } -u8 StoreData::GetGlassType() const { +GlassType StoreData::GetGlassType() const { return core_data.GetGlassType(); } -u8 StoreData::GetGlassColor() const { +CommonColor StoreData::GetGlassColor() const { return core_data.GetGlassColor(); } @@ -564,7 +564,7 @@ u8 StoreData::GetGlassY() const { return core_data.GetGlassY(); } -u8 StoreData::GetMoleType() const { +MoleType StoreData::GetMoleType() const { return core_data.GetMoleType(); } diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h index 1e010000b5..224c32cf8e 100644 --- a/src/core/hle/service/mii/types/store_data.h +++ b/src/core/hle/service/mii/types/store_data.h @@ -23,51 +23,51 @@ public: u32 IsValid() const; void SetFontRegion(FontRegion value); - void SetFavoriteColor(u8 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(u8 value); - void SetFacelineColor(u8 value); - void SetFacelineWrinkle(u8 value); - void SetFacelineMake(u8 value); - void SetHairType(u8 value); - void SetHairColor(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(u8 value); - void SetEyeColor(u8 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(u8 value); - void SetEyebrowColor(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(u8 value); + void SetNoseType(NoseType value); void SetNoseScale(u8 value); void SetNoseY(u8 value); void SetMouthType(u8 value); - void SetMouthColor(u8 value); + void SetMouthColor(CommonColor value); void SetMouthScale(u8 value); void SetMouthAspect(u8 value); void SetMouthY(u8 value); - void SetBeardColor(u8 value); + void SetBeardColor(CommonColor value); void SetBeardType(BeardType value); void SetMustacheType(MustacheType value); void SetMustacheScale(u8 value); void SetMustacheY(u8 value); - void SetGlassType(u8 value); - void SetGlassColor(u8 value); + void SetGlassType(GlassType value); + void SetGlassColor(CommonColor value); void SetGlassScale(u8 value); void SetGlassY(u8 value); - void SetMoleType(u8 value); + void SetMoleType(MoleType value); void SetMoleScale(u8 value); void SetMoleX(u8 value); void SetMoleY(u8 value); @@ -76,51 +76,51 @@ public: Common::UUID GetCreateId() const; FontRegion GetFontRegion() const; - u8 GetFavoriteColor() const; - u8 GetGender() const; + FavoriteColor GetFavoriteColor() const; + Gender GetGender() const; u8 GetHeight() const; u8 GetBuild() const; u8 GetType() const; u8 GetRegionMove() const; - u8 GetFacelineType() const; - u8 GetFacelineColor() const; - u8 GetFacelineWrinkle() const; - u8 GetFacelineMake() const; - u8 GetHairType() const; - u8 GetHairColor() const; - u8 GetHairFlip() const; - u8 GetEyeType() const; - u8 GetEyeColor() 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; - u8 GetEyebrowType() const; - u8 GetEyebrowColor() const; + EyebrowType GetEyebrowType() const; + CommonColor GetEyebrowColor() const; u8 GetEyebrowScale() const; u8 GetEyebrowAspect() const; u8 GetEyebrowRotate() const; u8 GetEyebrowX() const; u8 GetEyebrowY() const; - u8 GetNoseType() const; + NoseType GetNoseType() const; u8 GetNoseScale() const; u8 GetNoseY() const; - u8 GetMouthType() const; - u8 GetMouthColor() const; + MouthType GetMouthType() const; + CommonColor GetMouthColor() const; u8 GetMouthScale() const; u8 GetMouthAspect() const; u8 GetMouthY() const; - u8 GetBeardColor() const; - u8 GetBeardType() const; - u8 GetMustacheType() const; + CommonColor GetBeardColor() const; + BeardType GetBeardType() const; + MustacheType GetMustacheType() const; u8 GetMustacheScale() const; u8 GetMustacheY() const; - u8 GetGlassType() const; - u8 GetGlassColor() const; + GlassType GetGlassType() const; + CommonColor GetGlassColor() const; u8 GetGlassScale() const; u8 GetGlassY() const; - u8 GetMoleType() const; + MoleType GetMoleType() const; u8 GetMoleScale() const; u8 GetMoleX() const; u8 GetMoleY() const; diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index c7624520c1..1c28e0b1b5 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -9,14 +9,14 @@ namespace Service::Mii { void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) { - faceline_color = static_cast(store_data.GetFacelineColor() & 0xf); - hair_color = static_cast(store_data.GetHairColor() & 0x7f); - eye_color = static_cast(store_data.GetEyeColor() & 0x7f); - eyebrow_color = static_cast(store_data.GetEyebrowColor() & 0x7f); - mouth_color = static_cast(store_data.GetMouthColor() & 0x7f); - beard_color = static_cast(store_data.GetBeardColor() & 0x7f); - glass_color = static_cast(store_data.GetGlassColor() & 0x7f); - glass_type = static_cast(store_data.GetGlassType() & 0x1f); + faceline_color = static_cast(store_data.GetFacelineColor()) & 0xf; + hair_color = static_cast(store_data.GetHairColor()) & 0x7f; + eye_color = static_cast(store_data.GetEyeColor()) & 0x7f; + eyebrow_color = static_cast(store_data.GetEyebrowColor()) & 0x7f; + mouth_color = static_cast(store_data.GetMouthColor()) & 0x7f; + beard_color = static_cast(store_data.GetBeardColor()) & 0x7f; + glass_color = static_cast(store_data.GetGlassColor()) & 0x7f; + glass_type = static_cast(store_data.GetGlassType()) & 0x1f; } void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { @@ -28,8 +28,9 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { // TODO: We are ignoring a bunch of data from the mii_v3 - out_store_data.SetGender(static_cast(static_cast(mii_information.gender))); - out_store_data.SetFavoriteColor(static_cast(mii_information.favorite_color)); + out_store_data.SetGender(static_cast(mii_information.gender.Value())); + out_store_data.SetFavoriteColor( + static_cast(mii_information.favorite_color.Value())); out_store_data.SetHeight(height); out_store_data.SetBuild(build); @@ -37,56 +38,60 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { out_store_data.SetFontRegion( static_cast(static_cast(region_information.font_region))); - out_store_data.SetFacelineType(appearance_bits1.faceline_type); - out_store_data.SetFacelineColor(appearance_bits1.faceline_color); - out_store_data.SetFacelineWrinkle(appearance_bits2.faceline_wrinkle); - out_store_data.SetFacelineMake(appearance_bits2.faceline_make); + out_store_data.SetFacelineType( + static_cast(appearance_bits1.faceline_type.Value())); + out_store_data.SetFacelineColor( + static_cast(appearance_bits1.faceline_color.Value())); + out_store_data.SetFacelineWrinkle( + static_cast(appearance_bits2.faceline_wrinkle.Value())); + out_store_data.SetFacelineMake( + static_cast(appearance_bits2.faceline_make.Value())); - out_store_data.SetHairType(hair_type); - out_store_data.SetHairColor(appearance_bits3.hair_color); - out_store_data.SetHairFlip(static_cast(static_cast(appearance_bits3.hair_flip))); + out_store_data.SetHairType(static_cast(hair_type)); + out_store_data.SetHairColor(static_cast(appearance_bits3.hair_color.Value())); + out_store_data.SetHairFlip(static_cast(appearance_bits3.hair_flip.Value())); - out_store_data.SetEyeType(static_cast(appearance_bits4.eye_type)); - out_store_data.SetEyeColor(static_cast(appearance_bits4.eye_color)); + out_store_data.SetEyeType(static_cast(appearance_bits4.eye_type.Value())); + out_store_data.SetEyeColor(static_cast(appearance_bits4.eye_color.Value())); out_store_data.SetEyeScale(static_cast(appearance_bits4.eye_scale)); out_store_data.SetEyeAspect(static_cast(appearance_bits4.eye_aspect)); out_store_data.SetEyeRotate(static_cast(appearance_bits4.eye_rotate)); out_store_data.SetEyeX(static_cast(appearance_bits4.eye_x)); out_store_data.SetEyeY(static_cast(appearance_bits4.eye_y)); - out_store_data.SetEyebrowType(static_cast(appearance_bits5.eyebrow_type)); - out_store_data.SetEyebrowColor(static_cast(appearance_bits5.eyebrow_color)); + out_store_data.SetEyebrowType(static_cast(appearance_bits5.eyebrow_type.Value())); + out_store_data.SetEyebrowColor( + static_cast(appearance_bits5.eyebrow_color.Value())); out_store_data.SetEyebrowScale(static_cast(appearance_bits5.eyebrow_scale)); out_store_data.SetEyebrowAspect(static_cast(appearance_bits5.eyebrow_aspect)); out_store_data.SetEyebrowRotate(static_cast(appearance_bits5.eyebrow_rotate)); out_store_data.SetEyebrowX(static_cast(appearance_bits5.eyebrow_x)); out_store_data.SetEyebrowY(static_cast(appearance_bits5.eyebrow_y)); - out_store_data.SetNoseType(static_cast(appearance_bits6.nose_type)); + out_store_data.SetNoseType(static_cast(appearance_bits6.nose_type.Value())); out_store_data.SetNoseScale(static_cast(appearance_bits6.nose_scale)); out_store_data.SetNoseY(static_cast(appearance_bits6.nose_y)); out_store_data.SetMouthType(static_cast(appearance_bits7.mouth_type)); - out_store_data.SetMouthColor(static_cast(appearance_bits7.mouth_color)); + out_store_data.SetMouthColor(static_cast(appearance_bits7.mouth_color.Value())); out_store_data.SetMouthScale(static_cast(appearance_bits7.mouth_scale)); out_store_data.SetMouthAspect(static_cast(appearance_bits7.mouth_aspect)); out_store_data.SetMouthY(static_cast(appearance_bits8.mouth_y)); out_store_data.SetMustacheType( - static_cast(static_cast(appearance_bits8.mustache_type))); + static_cast(appearance_bits8.mustache_type.Value())); out_store_data.SetMustacheScale(static_cast(appearance_bits9.mustache_scale)); out_store_data.SetMustacheY(static_cast(appearance_bits9.mustache_y)); - out_store_data.SetBeardType( - static_cast(static_cast(appearance_bits9.beard_type))); - out_store_data.SetBeardColor(static_cast(appearance_bits9.beard_color)); + out_store_data.SetBeardType(static_cast(appearance_bits9.beard_type.Value())); + out_store_data.SetBeardColor(static_cast(appearance_bits9.beard_color.Value())); - out_store_data.SetGlassType(static_cast(appearance_bits10.glass_type)); - out_store_data.SetGlassColor(static_cast(appearance_bits10.glass_color)); + out_store_data.SetGlassType(static_cast(appearance_bits10.glass_type.Value())); + out_store_data.SetGlassColor(static_cast(appearance_bits10.glass_color.Value())); out_store_data.SetGlassScale(static_cast(appearance_bits10.glass_scale)); out_store_data.SetGlassY(static_cast(appearance_bits10.glass_y)); - out_store_data.SetMoleType(static_cast(appearance_bits11.mole_type)); + out_store_data.SetMoleType(static_cast(appearance_bits11.mole_type.Value())); out_store_data.SetMoleScale(static_cast(appearance_bits11.mole_scale)); out_store_data.SetMoleX(static_cast(appearance_bits11.mole_x)); out_store_data.SetMoleY(static_cast(appearance_bits11.mole_y)); @@ -94,71 +99,75 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { version = 1; - mii_information.gender.Assign(store_data.GetGender()); - mii_information.favorite_color.Assign(store_data.GetFavoriteColor()); + mii_information.gender.Assign(static_cast(store_data.GetGender())); + mii_information.favorite_color.Assign(static_cast(store_data.GetFavoriteColor())); height = store_data.GetHeight(); build = store_data.GetBuild(); mii_name = store_data.GetNickname(); region_information.font_region.Assign(static_cast(store_data.GetFontRegion())); - appearance_bits1.faceline_type.Assign(store_data.GetFacelineType()); - appearance_bits2.faceline_wrinkle.Assign(store_data.GetFacelineWrinkle()); - appearance_bits2.faceline_make.Assign(store_data.GetFacelineMake()); + appearance_bits1.faceline_type.Assign(static_cast(store_data.GetFacelineType())); + appearance_bits2.faceline_wrinkle.Assign(static_cast(store_data.GetFacelineWrinkle())); + appearance_bits2.faceline_make.Assign(static_cast(store_data.GetFacelineMake())); - hair_type = store_data.GetHairType(); - appearance_bits3.hair_flip.Assign(store_data.GetHairFlip()); + hair_type = static_cast(store_data.GetHairType()); + appearance_bits3.hair_flip.Assign(static_cast(store_data.GetHairFlip())); - appearance_bits4.eye_type.Assign(store_data.GetEyeType()); + appearance_bits4.eye_type.Assign(static_cast(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(store_data.GetEyebrowType()); + appearance_bits5.eyebrow_type.Assign(static_cast(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(store_data.GetNoseType()); + appearance_bits6.nose_type.Assign(static_cast(store_data.GetNoseType())); appearance_bits6.nose_scale.Assign(store_data.GetNoseScale()); appearance_bits6.nose_y.Assign(store_data.GetNoseY()); - appearance_bits7.mouth_type.Assign(store_data.GetMouthType()); + appearance_bits7.mouth_type.Assign(static_cast(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(store_data.GetMustacheType()); + appearance_bits8.mustache_type.Assign(static_cast(store_data.GetMustacheType())); appearance_bits9.mustache_scale.Assign(store_data.GetMustacheScale()); appearance_bits9.mustache_y.Assign(store_data.GetMustacheY()); - appearance_bits9.beard_type.Assign(store_data.GetBeardType()); + appearance_bits9.beard_type.Assign(static_cast(store_data.GetBeardType())); appearance_bits10.glass_scale.Assign(store_data.GetGlassScale()); appearance_bits10.glass_y.Assign(store_data.GetGlassY()); - appearance_bits11.mole_type.Assign(store_data.GetMoleType()); + appearance_bits11.mole_type.Assign(static_cast(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(store_data.GetFacelineColor())); - appearance_bits3.hair_color.Assign(RawData::FromVer3GetHairColor(store_data.GetHairColor())); - appearance_bits4.eye_color.Assign(RawData::FromVer3GetEyeColor(store_data.GetEyeColor())); + RawData::FromVer3GetFacelineColor(static_cast(store_data.GetFacelineColor()))); + appearance_bits3.hair_color.Assign( + RawData::FromVer3GetHairColor(static_cast(store_data.GetHairColor()))); + appearance_bits4.eye_color.Assign( + RawData::FromVer3GetEyeColor(static_cast(store_data.GetEyeColor()))); appearance_bits5.eyebrow_color.Assign( - RawData::FromVer3GetHairColor(store_data.GetEyebrowColor())); + RawData::FromVer3GetHairColor(static_cast(store_data.GetEyebrowColor()))); appearance_bits7.mouth_color.Assign( - RawData::FromVer3GetMouthlineColor(store_data.GetMouthColor())); - appearance_bits9.beard_color.Assign(RawData::FromVer3GetHairColor(store_data.GetBeardColor())); + RawData::FromVer3GetMouthlineColor(static_cast(store_data.GetMouthColor()))); + appearance_bits9.beard_color.Assign( + RawData::FromVer3GetHairColor(static_cast(store_data.GetBeardColor()))); appearance_bits10.glass_color.Assign( - RawData::FromVer3GetGlassColor(store_data.GetGlassColor())); - appearance_bits10.glass_type.Assign(RawData::FromVer3GetGlassType(store_data.GetGlassType())); + RawData::FromVer3GetGlassColor(static_cast(store_data.GetGlassColor()))); + appearance_bits10.glass_type.Assign( + RawData::FromVer3GetGlassType(static_cast(store_data.GetGlassType()))); crc = MiiUtil::CalculateCrc16(&version, sizeof(Ver3StoreData) - sizeof(u16)); } @@ -166,64 +175,65 @@ void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { 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_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 < 12); - is_valid = is_valid && (height < 128); - is_valid = is_valid && (build < 128); + is_valid = is_valid && (mii_information.favorite_color <= static_cast(FavoriteColor::Max)); + is_valid = is_valid && (height <= MaxHeight); + is_valid = is_valid && (build <= MaxBuild); - is_valid = is_valid && (appearance_bits1.faceline_type < 12); - is_valid = is_valid && (appearance_bits1.faceline_color < 7); - is_valid = is_valid && (appearance_bits2.faceline_wrinkle < 12); - is_valid = is_valid && (appearance_bits2.faceline_make < 12); + is_valid = is_valid && (appearance_bits1.faceline_type <= static_cast(FacelineType::Max)); + is_valid = is_valid && (appearance_bits1.faceline_color <= MaxVer3CommonColor - 2); + is_valid = + is_valid && (appearance_bits2.faceline_wrinkle <= static_cast(FacelineWrinkle::Max)); + is_valid = is_valid && (appearance_bits2.faceline_make <= static_cast(FacelineMake::Max)); - is_valid = is_valid && (hair_type < 132); - is_valid = is_valid && (appearance_bits3.hair_color < 8); + is_valid = is_valid && (hair_type <= static_cast(HairType::Max)); + is_valid = is_valid && (appearance_bits3.hair_color <= MaxVer3CommonColor); - is_valid = is_valid && (appearance_bits4.eye_type < 60); - is_valid = is_valid && (appearance_bits4.eye_color < 6); - is_valid = is_valid && (appearance_bits4.eye_scale < 8); - is_valid = is_valid && (appearance_bits4.eye_aspect < 7); - is_valid = is_valid && (appearance_bits4.eye_rotate < 8); - is_valid = is_valid && (appearance_bits4.eye_x < 13); - is_valid = is_valid && (appearance_bits4.eye_y < 19); + is_valid = is_valid && (appearance_bits4.eye_type <= static_cast(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 < 25); - is_valid = is_valid && (appearance_bits5.eyebrow_color < 8); - is_valid = is_valid && (appearance_bits5.eyebrow_scale < 9); - is_valid = is_valid && (appearance_bits5.eyebrow_aspect < 7); - is_valid = is_valid && (appearance_bits5.eyebrow_rotate < 12); - is_valid = is_valid && (appearance_bits5.eyebrow_x < 12); - is_valid = is_valid && (appearance_bits5.eyebrow_y < 19); + is_valid = is_valid && (appearance_bits5.eyebrow_type <= static_cast(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 < 18); - is_valid = is_valid && (appearance_bits6.nose_scale < 9); - is_valid = is_valid && (appearance_bits6.nose_y < 19); + is_valid = is_valid && (appearance_bits6.nose_type <= static_cast(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 < 36); - is_valid = is_valid && (appearance_bits7.mouth_color < 5); - is_valid = is_valid && (appearance_bits7.mouth_scale < 9); - is_valid = is_valid && (appearance_bits7.mouth_aspect < 7); - is_valid = is_valid && (appearance_bits8.mouth_y < 19); + is_valid = is_valid && (appearance_bits7.mouth_type <= static_cast(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 < 6); - is_valid = is_valid && (appearance_bits9.mustache_scale < 7); - is_valid = is_valid && (appearance_bits9.mustache_y < 17); + is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast(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 < 6); - is_valid = is_valid && (appearance_bits9.beard_color < 8); + is_valid = is_valid && (appearance_bits9.beard_type <= static_cast(BeardType::Max)); + is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor); - is_valid = is_valid && (appearance_bits10.glass_type < 9); - is_valid = is_valid && (appearance_bits10.glass_color < 6); - is_valid = is_valid && (appearance_bits10.glass_scale < 8); - is_valid = is_valid && (appearance_bits10.glass_y < 21); + 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 < 2); - is_valid = is_valid && (appearance_bits11.mole_scale < 9); - is_valid = is_valid && (appearance_bits11.mole_x < 17); - is_valid = is_valid && (appearance_bits11.mole_y < 31); + is_valid = is_valid && (appearance_bits11.mole_type <= static_cast(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; } From f8985d1cc5f7378ab2bca2526a61d6aff778d36e Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 11 Sep 2023 23:50:36 -0400 Subject: [PATCH 33/69] qt: add verification for installed contents --- src/core/file_sys/nca_metadata.cpp | 4 + src/core/file_sys/nca_metadata.h | 1 + src/core/file_sys/registered_cache.cpp | 28 ++++++- src/core/file_sys/registered_cache.h | 5 ++ src/yuzu/main.cpp | 104 +++++++++++++++++++++++++ src/yuzu/main.h | 1 + src/yuzu/main.ui | 6 ++ 7 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 52c78020c3..f4a7746758 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 c59ece0106..68e463b5f9 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -89,6 +89,7 @@ public: std::vector content_records_, std::vector 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 f70adab82b..e33b00d89f 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 bd7f53eaf3..64815a8457 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/yuzu/main.cpp b/src/yuzu/main.cpp index 9cea60c326..fc2055a7bf 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "core/loader/nca.h" #ifdef __APPLE__ #include // for chdir #endif @@ -1554,6 +1555,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); } @@ -4000,6 +4002,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 failed; + + // Declare progress callback. + auto QtProgressCallback = [&](size_t nca_processed, size_t nca_total) { + if (progress.wasCanceled()) { + return false; + } + progress.setValue(static_cast(((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 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 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 1e4f6e477e..7b4820ecdf 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -343,6 +343,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 013ba0ceb4..e54d7d75d8 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -148,6 +148,7 @@ + @@ -214,6 +215,11 @@ &Reinitialize keys... + + + Verify installed contents + + &About yuzu From 85e175472841689c1f0b60bd8d78f9d2215f3bf8 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:30:23 -0400 Subject: [PATCH 34/69] android/config: Remove uncaught usage of stoul --- src/android/app/src/main/jni/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 34b425cb45..81120ab0f1 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -282,7 +282,7 @@ void Config::ReadValues() { std::stringstream ss(title_list); std::string line; while (std::getline(ss, line, '|')) { - const auto title_id = std::stoul(line, nullptr, 16); + const auto title_id = std::strtoul(line.c_str(), nullptr, 16); const auto disabled_list = config->Get("AddOns", "disabled_" + line, ""); std::stringstream inner_ss(disabled_list); From 0098ecb6090b767f13683136c28fee97b8b127c7 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:31:46 -0400 Subject: [PATCH 35/69] settings: Retro-port Citra Settings work This has yet to be PR'd on Citra, but regressions on yuzu that have been fixed in Citra needed to appear here. --- src/common/settings_common.h | 10 ++++++++++ src/common/settings_setting.h | 36 +++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 5b170dfd5d..067234aab0 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -225,6 +225,16 @@ public: */ [[nodiscard]] virtual constexpr u32 EnumIndex() const = 0; + /** + * @returns True if the underlying type is a floating point storage + */ + [[nodiscard]] virtual constexpr bool IsFloatingPoint() const = 0; + + /** + * @returns True if the underlying type is a integer storage + */ + [[nodiscard]] virtual constexpr bool IsIntegral() const = 0; + /* * Switchable settings */ diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h index e10843c73c..79d2b715f1 100644 --- a/src/common/settings_setting.h +++ b/src/common/settings_setting.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "common/common_types.h" #include "common/settings_common.h" #include "common/settings_enums.h" @@ -112,11 +113,12 @@ protected: return value_.has_value() ? std::to_string(*value_) : "none"; } else if constexpr (std::is_same_v) { return value_ ? "true" : "false"; - } else if constexpr (std::is_same_v) { - // Compatibility with old AudioEngine setting being a string - return CanonicalizeEnum(value_); + } else if constexpr (std::is_floating_point_v) { + return fmt::format("{:f}", value_); + } else if constexpr (std::is_enum_v) { + return std::to_string(static_cast(value_)); } else { - return std::to_string(static_cast(value_)); + return std::to_string(value_); } } @@ -180,13 +182,15 @@ public: this->SetValue(static_cast(std::stoul(input))); } else if constexpr (std::is_same_v) { this->SetValue(input == "true"); - } else if constexpr (std::is_same_v) { - this->SetValue(ToEnum(input)); + } else if constexpr (std::is_same_v) { + this->SetValue(std::stof(input)); } else { this->SetValue(static_cast(std::stoll(input))); } } catch (std::invalid_argument&) { this->SetValue(this->GetDefault()); + } catch (std::out_of_range&) { + this->SetValue(this->GetDefault()); } } @@ -215,11 +219,27 @@ public: } } + [[nodiscard]] constexpr bool IsFloatingPoint() const final { + return std::is_floating_point_v; + } + + [[nodiscard]] constexpr bool IsIntegral() const final { + return std::is_integral_v; + } + [[nodiscard]] std::string MinVal() const override final { - return this->ToString(minimum); + if constexpr (std::is_arithmetic_v && !ranged) { + return this->ToString(std::numeric_limits::min()); + } else { + return this->ToString(minimum); + } } [[nodiscard]] std::string MaxVal() const override final { - return this->ToString(maximum); + if constexpr (std::is_arithmetic_v && !ranged) { + return this->ToString(std::numeric_limits::max()); + } else { + return this->ToString(maximum); + } } [[nodiscard]] constexpr bool Ranged() const override { From 7f98f4a38bf206bef9a838f6195ae3e0c6271070 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:32:14 -0400 Subject: [PATCH 36/69] key_manager: Remove uncaught usage of stoul --- src/core/crypto/key_manager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index e13c5cdc7c..43a3c5ffd6 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -724,14 +724,14 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti continue; } - const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16); + const auto index = std::strtoul(out[0].substr(8, 2).c_str(), nullptr, 16); keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { if (!ValidCryptoRevisionString(out[0], 18, 2)) { continue; } - const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); + const auto index = std::strtoul(out[0].substr(18, 2).c_str(), nullptr, 16); encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { eticket_extended_kek = Common::HexStringToArray<576>(out[1]); @@ -750,7 +750,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti } if (out[0].compare(0, kv.second.size(), kv.second) == 0) { const auto index = - std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); + std::strtoul(out[0].substr(kv.second.size(), 2).c_str(), nullptr, 16); const auto sub = kv.first.second; if (sub == 0) { s128_keys[{kv.first.first, index, 0}] = @@ -770,7 +770,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti const auto& match = kak_names[j]; if (out[0].compare(0, std::strlen(match), match) == 0) { const auto index = - std::stoul(out[0].substr(std::strlen(match), 2), nullptr, 16); + std::strtoul(out[0].substr(std::strlen(match), 2).c_str(), nullptr, 16); s128_keys[{S128KeyType::KeyArea, index, j}] = Common::HexStringToArray<16>(out[1]); } From 22be3008f88ed147145c21566c6ae1ac91985683 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:32:48 -0400 Subject: [PATCH 37/69] ips_layer: Remove uncaught usage of stoul/ll --- src/core/file_sys/ips_layer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index efdf18cee8..7be1322cc1 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -165,7 +165,7 @@ static std::string EscapeStringSequences(std::string in) { void IPSwitchCompiler::ParseFlag(const std::string& line) { if (StartsWith(line, "@flag offset_shift ")) { // Offset Shift Flag - offset_shift = std::stoll(line.substr(19), nullptr, 0); + offset_shift = std::strtoll(line.substr(19).c_str(), nullptr, 0); } else if (StartsWith(line, "@little-endian")) { // Set values to read as little endian is_little_endian = true; @@ -263,7 +263,7 @@ void IPSwitchCompiler::Parse() { // 11 - 8 hex digit offset + space + minimum two digit overwrite val if (patch_line.length() < 11) break; - auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16); + auto offset = std::strtoul(patch_line.substr(0, 8).c_str(), nullptr, 16); offset += static_cast(offset_shift); std::vector replace; From 0eef4a6c948d020b9c9ae7aa5a314ff5b7c04c27 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:51:58 -0400 Subject: [PATCH 38/69] cheat_engine: Remove uncaught usage of stoul --- src/core/memory/cheat_engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 7b52f61a7f..a06e991667 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -154,7 +154,7 @@ std::vector TextCheatParser::Parse(std::string_view data) const { return {}; } - const auto value = static_cast(std::stoul(hex, nullptr, 0x10)); + const auto value = static_cast(std::strtoul(hex.c_str(), nullptr, 0x10)); out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] = value; From 2d2c176f030fd9c0559a2cf983296e16c4b835c7 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:34:01 -0400 Subject: [PATCH 39/69] configure_ui: Remove unnecessary usage of stoul --- src/yuzu/configuration/configure_ui.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 34ab016171..a9fde9f4f3 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -4,6 +4,7 @@ #include "yuzu/configuration/configure_ui.h" #include +#include #include #include #include @@ -94,11 +95,7 @@ static void PopulateResolutionComboBox(QComboBox* screenshot_height, QWidget* pa } static u32 ScreenshotDimensionToInt(const QString& height) { - try { - return std::stoi(height.toStdString()); - } catch (std::invalid_argument&) { - return 0; - } + return std::strtoul(height.toUtf8(), nullptr, 0); } ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent) From 3ae3706c847431089389721e2873bc9a0549d6d9 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:50:25 -0400 Subject: [PATCH 40/69] shared_widget: Forward-port Citra changes Seemed like a good time to move these over. Also remove usage of std::sto{l,ll,ul,f,d} --- src/yuzu/configuration/shared_widget.cpp | 157 ++++++++++++++++++----- src/yuzu/configuration/shared_widget.h | 8 ++ 2 files changed, 134 insertions(+), 31 deletions(-) diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index d63093985b..e5e071386b 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp @@ -151,7 +151,7 @@ QWidget* Widget::CreateCombobox(std::function& serializer, return -1; }; - const u32 setting_value = std::stoi(setting.ToString()); + const u32 setting_value = std::strtoul(setting.ToString().c_str(), nullptr, 0); combobox->setCurrentIndex(find_index(setting_value)); serializer = [this, enumeration]() { @@ -160,7 +160,7 @@ QWidget* Widget::CreateCombobox(std::function& serializer, }; restore_func = [this, find_index]() { - const u32 global_value = std::stoi(RelevantDefault(setting)); + const u32 global_value = std::strtoul(RelevantDefault(setting).c_str(), nullptr, 0); combobox->setCurrentIndex(find_index(global_value)); }; @@ -209,7 +209,7 @@ QWidget* Widget::CreateRadioGroup(std::function& serializer, } }; - const u32 setting_value = std::stoi(setting.ToString()); + const u32 setting_value = std::strtoul(setting.ToString().c_str(), nullptr, 0); set_index(setting_value); serializer = [get_selected]() { @@ -218,7 +218,7 @@ QWidget* Widget::CreateRadioGroup(std::function& serializer, }; restore_func = [this, set_index]() { - const u32 global_value = std::stoi(RelevantDefault(setting)); + const u32 global_value = std::strtoul(RelevantDefault(setting).c_str(), nullptr, 0); set_index(global_value); }; @@ -255,6 +255,58 @@ QWidget* Widget::CreateLineEdit(std::function& serializer, return line_edit; } +static void CreateIntSlider(Settings::BasicSetting& setting, bool reversed, float multiplier, + QLabel* feedback, const QString& use_format, QSlider* slider, + std::function& serializer, + std::function& restore_func) { + int max_val = std::strtol(setting.MaxVal().c_str(), nullptr, 0); + + const auto update_feedback = [=](int value) { + int present = (reversed ? max_val - value : value) * multiplier + 0.5f; + feedback->setText(use_format.arg(QVariant::fromValue(present).value())); + }; + + QObject::connect(slider, &QAbstractSlider::valueChanged, update_feedback); + update_feedback(std::strtol(setting.ToString().c_str(), nullptr, 0)); + + slider->setMinimum(std::strtol(setting.MinVal().c_str(), nullptr, 0)); + slider->setMaximum(max_val); + slider->setValue(std::strtol(setting.ToString().c_str(), nullptr, 0)); + + serializer = [slider]() { return std::to_string(slider->value()); }; + restore_func = [slider, &setting]() { + slider->setValue(std::strtol(RelevantDefault(setting).c_str(), nullptr, 0)); + }; +} + +static void CreateFloatSlider(Settings::BasicSetting& setting, bool reversed, float multiplier, + QLabel* feedback, const QString& use_format, QSlider* slider, + std::function& serializer, + std::function& restore_func) { + float max_val = std::strtof(setting.MaxVal().c_str(), nullptr); + float min_val = std::strtof(setting.MinVal().c_str(), nullptr); + float use_multiplier = multiplier == default_multiplier ? default_float_multiplier : multiplier; + + const auto update_feedback = [=](float value) { + int present = (reversed ? max_val - value : value) + 0.5f; + feedback->setText(use_format.arg(QVariant::fromValue(present).value())); + }; + + QObject::connect(slider, &QAbstractSlider::valueChanged, update_feedback); + update_feedback(std::strtof(setting.ToString().c_str(), nullptr)); + + slider->setMinimum(min_val * use_multiplier); + slider->setMaximum(max_val * use_multiplier); + slider->setValue(std::strtof(setting.ToString().c_str(), nullptr) * use_multiplier); + + serializer = [slider, use_multiplier]() { + return std::to_string(slider->value() / use_multiplier); + }; + restore_func = [slider, &setting, use_multiplier]() { + slider->setValue(std::strtof(RelevantDefault(setting).c_str(), nullptr) * use_multiplier); + }; +} + QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& given_suffix, std::function& serializer, std::function& restore_func, @@ -278,27 +330,20 @@ QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& gi layout->setContentsMargins(0, 0, 0, 0); - int max_val = std::stoi(setting.MaxVal()); - QString suffix = given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; const QString use_format = QStringLiteral("%1").append(suffix); - QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) { - int present = (reversed ? max_val - value : value) * multiplier + 0.5f; - feedback->setText(use_format.arg(QVariant::fromValue(present).value())); - }); - - slider->setMinimum(std::stoi(setting.MinVal())); - slider->setMaximum(max_val); - slider->setValue(std::stoi(setting.ToString())); + if (setting.IsIntegral()) { + CreateIntSlider(setting, reversed, multiplier, feedback, use_format, slider, serializer, + restore_func); + } else { + CreateFloatSlider(setting, reversed, multiplier, feedback, use_format, slider, serializer, + restore_func); + } slider->setInvertedAppearance(reversed); - slider->setInvertedControls(reversed); - - serializer = [this]() { return std::to_string(slider->value()); }; - restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); }; if (!Settings::IsConfiguringGlobal()) { QObject::connect(slider, &QAbstractSlider::actionTriggered, [touch]() { touch(); }); @@ -311,11 +356,9 @@ QWidget* Widget::CreateSpinBox(const QString& given_suffix, std::function& serializer, std::function& restore_func, const std::function& touch) { - const int min_val = - setting.Ranged() ? std::stoi(setting.MinVal()) : std::numeric_limits::min(); - const int max_val = - setting.Ranged() ? std::stoi(setting.MaxVal()) : std::numeric_limits::max(); - const int default_val = std::stoi(setting.ToString()); + const auto min_val = std::strtol(setting.MinVal().c_str(), nullptr, 0); + const auto max_val = std::strtol(setting.MaxVal().c_str(), nullptr, 0); + const auto default_val = std::strtol(setting.ToString().c_str(), nullptr, 0); QString suffix = given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; @@ -329,13 +372,13 @@ QWidget* Widget::CreateSpinBox(const QString& given_suffix, serializer = [this]() { return std::to_string(spinbox->value()); }; restore_func = [this]() { - auto value{std::stol(RelevantDefault(setting))}; + auto value{std::strtol(RelevantDefault(setting).c_str(), nullptr, 0)}; spinbox->setValue(value); }; if (!Settings::IsConfiguringGlobal()) { QObject::connect(spinbox, QOverload::of(&QSpinBox::valueChanged), [this, touch]() { - if (spinbox->value() != std::stoi(setting.ToStringGlobal())) { + if (spinbox->value() != std::strtol(setting.ToStringGlobal().c_str(), nullptr, 0)) { touch(); } }); @@ -344,6 +387,43 @@ QWidget* Widget::CreateSpinBox(const QString& given_suffix, return spinbox; } +QWidget* Widget::CreateDoubleSpinBox(const QString& given_suffix, + std::function& serializer, + std::function& restore_func, + const std::function& touch) { + const auto min_val = std::strtod(setting.MinVal().c_str(), nullptr); + const auto max_val = std::strtod(setting.MaxVal().c_str(), nullptr); + const auto default_val = std::strtod(setting.ToString().c_str(), nullptr); + + QString suffix = + given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; + + double_spinbox = new QDoubleSpinBox(this); + double_spinbox->setRange(min_val, max_val); + double_spinbox->setValue(default_val); + double_spinbox->setSuffix(suffix); + double_spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + serializer = [this]() { return fmt::format("{:f}", double_spinbox->value()); }; + + restore_func = [this]() { + auto value{std::strtod(RelevantDefault(setting).c_str(), nullptr)}; + double_spinbox->setValue(value); + }; + + if (!Settings::IsConfiguringGlobal()) { + QObject::connect(double_spinbox, QOverload::of(&QDoubleSpinBox::valueChanged), + [this, touch]() { + if (double_spinbox->value() != + std::strtod(setting.ToStringGlobal().c_str(), nullptr)) { + touch(); + } + }); + } + + return double_spinbox; +} + QWidget* Widget::CreateHexEdit(std::function& serializer, std::function& restore_func, const std::function& touch) { @@ -353,7 +433,8 @@ QWidget* Widget::CreateHexEdit(std::function& serializer, } auto to_hex = [=](const std::string& input) { - return QString::fromStdString(fmt::format("{:08x}", std::stoul(input))); + return QString::fromStdString( + fmt::format("{:08x}", std::strtoul(input.c_str(), nullptr, 0))); }; QRegularExpressionValidator* regex = new QRegularExpressionValidator( @@ -366,7 +447,7 @@ QWidget* Widget::CreateHexEdit(std::function& serializer, line_edit->setValidator(regex); auto hex_to_dec = [this]() -> std::string { - return std::to_string(std::stoul(line_edit->text().toStdString(), nullptr, 16)); + return std::to_string(std::strtoul(line_edit->text().toStdString().c_str(), nullptr, 16)); }; serializer = [hex_to_dec]() { return hex_to_dec(); }; @@ -386,7 +467,8 @@ QWidget* Widget::CreateDateTimeEdit(bool disabled, bool restrict, std::function& restore_func, const std::function& touch) { const long long current_time = QDateTime::currentSecsSinceEpoch(); - const s64 the_time = disabled ? current_time : std::stoll(setting.ToString()); + const s64 the_time = + disabled ? current_time : std::strtoll(setting.ToString().c_str(), nullptr, 0); const auto default_val = QDateTime::fromSecsSinceEpoch(the_time); date_time_edit = new QDateTimeEdit(this); @@ -399,7 +481,7 @@ QWidget* Widget::CreateDateTimeEdit(bool disabled, bool restrict, auto get_clear_val = [this, restrict, current_time]() { return QDateTime::fromSecsSinceEpoch([this, restrict, current_time]() { if (restrict && checkbox->checkState() == Qt::Checked) { - return std::stoll(RelevantDefault(setting)); + return std::strtoll(RelevantDefault(setting).c_str(), nullptr, 0); } return current_time; }()); @@ -506,8 +588,7 @@ void Widget::SetupComponent(const QString& label, std::function& load_fu } else { data_component = CreateCombobox(serializer, restore_func, touch); } - } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) || - type == typeid(s64) || type == typeid(u8)) { + } else if (setting.IsIntegral()) { switch (request) { case RequestType::Slider: case RequestType::ReverseSlider: @@ -534,6 +615,20 @@ void Widget::SetupComponent(const QString& label, std::function& load_fu default: UNIMPLEMENTED(); } + } else if (setting.IsFloatingPoint()) { + switch (request) { + case RequestType::Default: + case RequestType::SpinBox: + data_component = CreateDoubleSpinBox(suffix, serializer, restore_func, touch); + break; + case RequestType::Slider: + case RequestType::ReverseSlider: + data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, suffix, + serializer, restore_func, touch); + break; + default: + UNIMPLEMENTED(); + } } else if (type == typeid(std::string)) { switch (request) { case RequestType::Default: diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h index 5303dd898d..86edaacc88 100644 --- a/src/yuzu/configuration/shared_widget.h +++ b/src/yuzu/configuration/shared_widget.h @@ -22,6 +22,7 @@ class QObject; class QPushButton; class QSlider; class QSpinBox; +class QDoubleSpinBox; class QRadioButton; namespace Settings { @@ -43,6 +44,9 @@ enum class RequestType { MaxEnum, }; +constexpr const float default_multiplier{1.f}; +constexpr const float default_float_multiplier{100.f}; + class Widget : public QWidget { Q_OBJECT @@ -89,6 +93,7 @@ public: QPushButton* restore_button{}; ///< Restore button for custom configurations QLineEdit* line_edit{}; ///< QLineEdit, used for LineEdit and HexEdit QSpinBox* spinbox{}; + QDoubleSpinBox* double_spinbox{}; QCheckBox* checkbox{}; QSlider* slider{}; QComboBox* combobox{}; @@ -126,6 +131,9 @@ private: const std::function& touch); QWidget* CreateSpinBox(const QString& suffix, std::function& serializer, std::function& restore_func, const std::function& touch); + QWidget* CreateDoubleSpinBox(const QString& suffix, std::function& serializer, + std::function& restore_func, + const std::function& touch); QWidget* parent; const TranslationMap& translations; From 5ffa1049ae639215f4885684a39607875cb9497b Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:35:36 -0400 Subject: [PATCH 41/69] cmd/config: Remove uncaught usage of stoul --- src/yuzu_cmd/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index c42d987090..0d25ff4001 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -259,7 +259,7 @@ void Config::ReadValues() { std::stringstream ss(title_list); std::string line; while (std::getline(ss, line, '|')) { - const auto title_id = std::stoul(line, nullptr, 16); + const auto title_id = std::strtoul(line.c_str(), nullptr, 16); const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); std::stringstream inner_ss(disabled_list); From c2961454fe006265454448ecce3f0a34834ec691 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:36:09 -0400 Subject: [PATCH 42/69] cmd/yuzu: Remove uncaught usage of stoi Also fixes a style inconsistency --- src/yuzu_cmd/yuzu.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 55d0938f7e..087cfaa26e 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -264,8 +264,9 @@ int main(int argc, char** argv) { nickname = match[1]; password = match[2]; address = match[3]; - if (!match[4].str().empty()) - port = static_cast(std::stoi(match[4])); + if (!match[4].str().empty()) { + port = static_cast(std::strtoul(match[4].str().c_str(), nullptr, 0)); + } std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); if (!std::regex_match(nickname, nickname_re)) { std::cout From fe70c6f4812a7c8076be3d8c12cc914f7c3b6d19 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:59:44 -0400 Subject: [PATCH 43/69] settings_setting: Don't remove the AudioEngine workaround --- src/common/settings_setting.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h index 79d2b715f1..7be6f26f7a 100644 --- a/src/common/settings_setting.h +++ b/src/common/settings_setting.h @@ -113,6 +113,9 @@ protected: return value_.has_value() ? std::to_string(*value_) : "none"; } else if constexpr (std::is_same_v) { return value_ ? "true" : "false"; + } else if constexpr (std::is_same_v) { + // Compatibility with old AudioEngine setting being a string + return CanonicalizeEnum(value_); } else if constexpr (std::is_floating_point_v) { return fmt::format("{:f}", value_); } else if constexpr (std::is_enum_v) { From 9d7eebde7b06f3a921707dfb7ebdee22ab9d257e Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 13 Sep 2023 22:06:33 -0400 Subject: [PATCH 44/69] ngc: implement service --- src/common/logging/filter.cpp | 2 +- src/common/logging/types.h | 2 +- src/core/CMakeLists.txt | 4 +- src/core/hle/service/ngc/ngc.cpp | 150 ++++++++++++++++++ .../hle/service/{ngct/ngct.h => ngc/ngc.h} | 4 +- src/core/hle/service/ngct/ngct.cpp | 62 -------- src/core/hle/service/service.cpp | 4 +- 7 files changed, 158 insertions(+), 70 deletions(-) create mode 100644 src/core/hle/service/ngc/ngc.cpp rename src/core/hle/service/{ngct/ngct.h => ngc/ngc.h} (78%) delete mode 100644 src/core/hle/service/ngct/ngct.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index c959095613..4e3a614a45 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 8356e3183a..08af50ee0c 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/core/CMakeLists.txt b/src/core/CMakeLists.txt index c33910ade9..6cd1a28f2d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -627,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 diff --git a/src/core/hle/service/ngc/ngc.cpp b/src/core/hle/service/ngc/ngc.cpp new file mode 100644 index 0000000000..c26019ec09 --- /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 { +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(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(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 { +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(); + [[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(); + 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(system); + + server_manager->RegisterNamedService("ngct:u", std::make_shared(system)); + server_manager->RegisterNamedService("ngc:u", std::make_shared(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 similarity index 78% rename from src/core/hle/service/ngct/ngct.h rename to src/core/hle/service/ngc/ngc.h index 27c34dad45..823b1aa819 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 493c80ed27..0000000000 --- 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 { -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(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(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(system); - - server_manager->RegisterNamedService("ngct:u", std::make_shared(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 69cdb59185..0ad6073912 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, 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); }); From eb4ddb2868a6cdba6ef633f9590ff9627b831102 Mon Sep 17 00:00:00 2001 From: liamwhite Date: Thu, 14 Sep 2023 09:23:50 -0400 Subject: [PATCH 45/69] shader_recompiler: skip sampler for buffer textures (#11435) --- src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | 4 +--- src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | 3 +-- src/shader_recompiler/backend/spirv/spirv_emit_context.h | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 34240b36f7..8decdf3992 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 238fb40e3d..72f69b7aaa 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 e63330f112..7c49fd5045 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 cbufs{}; From c656105a6c6ce14ced695f8edb1864cbba4e66dd Mon Sep 17 00:00:00 2001 From: GPUCode Date: Sun, 10 Sep 2023 23:26:09 +0300 Subject: [PATCH 46/69] debug: Add renderdoc capture hotkey --- externals/CMakeLists.txt | 3 + externals/renderdoc/renderdoc_app.h | 744 +++++++++++++++++++++ src/common/settings.h | 2 + src/core/CMakeLists.txt | 5 +- src/core/core.cpp | 11 + src/core/core.h | 6 + src/core/tools/renderdoc.cpp | 55 ++ src/core/tools/renderdoc.h | 22 + src/yuzu/configuration/configure_debug.cpp | 3 + src/yuzu/configuration/configure_debug.ui | 119 ++-- src/yuzu/hotkeys.h | 4 +- src/yuzu/main.cpp | 6 + 12 files changed, 922 insertions(+), 58 deletions(-) create mode 100644 externals/renderdoc/renderdoc_app.h create mode 100644 src/core/tools/renderdoc.cpp create mode 100644 src/core/tools/renderdoc.h diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 82a6da9fde..a4c2ffc107 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 0000000000..0f4a1f98b3 --- /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 +#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/common/settings.h b/src/common/settings.h index b15213bd72..82ec9077ea 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -348,6 +348,8 @@ struct Values { Category::RendererDebug}; Setting disable_shader_loop_safety_checks{ linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug}; + Setting enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey", + Category::RendererDebug}; // System SwitchableSetting language_index{linkage, diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6cd1a28f2d..30d2f7df64 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -864,6 +864,8 @@ add_library(core STATIC telemetry_session.h tools/freezer.cpp tools/freezer.h + tools/renderdoc.cpp + tools/renderdoc.h ) if (MSVC) @@ -879,6 +881,7 @@ else() -Werror=conversion -Wno-sign-conversion + -Wno-cast-function-type $<$:-fsized-deallocation> ) @@ -887,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 2d6e613986..e8300cd05d 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(); + } + LOG_DEBUG(Core, "Initialized OK"); return SystemResultStatus::Success; @@ -521,6 +526,8 @@ struct System::Impl { std::unique_ptr memory_freezer; std::array build_id{}; + std::unique_ptr renderdoc_api; + /// Frontend applets Service::AM::Applets::AppletManager applet_manager; @@ -1024,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&& server_manager) { return impl->kernel.RunServer(std::move(server_manager)); } diff --git a/src/core/core.h b/src/core/core.h index fba3121257..df20f26f3c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -102,6 +102,10 @@ namespace Network { class RoomNetwork; } +namespace Tools { +class RenderdocAPI; +} + namespace Core { class ARM_Interface; @@ -413,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; diff --git a/src/core/tools/renderdoc.cpp b/src/core/tools/renderdoc.cpp new file mode 100644 index 0000000000..44d24822ab --- /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 + +#include "common/assert.h" +#include "common/dynamic_library.h" +#include "core/tools/renderdoc.h" + +#ifdef WIN32 +#include +#else +#include +#endif + +namespace Tools { + +RenderdocAPI::RenderdocAPI() { +#ifdef WIN32 + if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) { + const auto RENDERDOC_GetAPI = + reinterpret_cast(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(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 0000000000..0e5e43da5b --- /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/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index cbeb8f1682..b22fda7462 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 97c7d9022e..66b8b74591 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -18,8 +18,8 @@ 0 0 - 829 - 758 + 842 + 741 @@ -260,7 +260,7 @@ Graphics - + When checked, it executes shaders without loop logic changes @@ -270,20 +270,7 @@ - - - - true - - - When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - - - Dump Game Shaders - - - - + true @@ -296,33 +283,7 @@ - - - - true - - - When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - - - Disable Macro JIT - - - - - - - true - - - When checked, the graphics API enters a slower debugging mode - - - Enable Graphics Debugging - - - - + true @@ -335,17 +296,7 @@ - - - - When checked, yuzu will log statistics about the compiled pipeline cache - - - Enable Shader Feedback - - - - + When checked, it enables Nsight Aftermath crash dumps @@ -355,7 +306,30 @@ - + + + + When checked, yuzu will log statistics about the compiled pipeline cache + + + Enable Shader Feedback + + + + + + + true + + + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower + + + Disable Macro JIT + + + + Qt::Vertical @@ -371,6 +345,39 @@ + + + + true + + + When checked, the graphics API enters a slower debugging mode + + + Enable Graphics Debugging + + + + + + + true + + + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found + + + Dump Game Shaders + + + + + + + Enable Renderdoc Hotkey + + + diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index 848239c35d..56eee8d821 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h @@ -4,10 +4,12 @@ #pragma once #include +#include +#include +#include #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 97d2166387..d32aa9615b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -9,6 +9,7 @@ #include #include #include "core/loader/nca.h" +#include "core/tools/renderdoc.h" #ifdef __APPLE__ #include // for chdir #endif @@ -1348,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; From fea5b758bce5f886f6ccadd5736a4ba2c17514cb Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Thu, 14 Sep 2023 11:30:14 -0400 Subject: [PATCH 47/69] settings_common: Fix typo --- src/common/settings_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 067234aab0..1800ab10dd 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -231,7 +231,7 @@ public: [[nodiscard]] virtual constexpr bool IsFloatingPoint() const = 0; /** - * @returns True if the underlying type is a integer storage + * @returns True if the underlying type is an integer storage */ [[nodiscard]] virtual constexpr bool IsIntegral() const = 0; From 5d52d73c4b2781fd99260534c17e35640ab17589 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Thu, 14 Sep 2023 11:31:26 -0400 Subject: [PATCH 48/69] shared_widget: Use default literals more --- src/yuzu/configuration/shared_widget.cpp | 26 +++++++++++------------- src/yuzu/configuration/shared_widget.h | 17 +++++++++------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index e5e071386b..ea8d7add42 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp @@ -63,7 +63,7 @@ static QString DefaultSuffix(QWidget* parent, Settings::BasicSetting& setting) { return tr("%", context.c_str()); } - return QStringLiteral(""); + return default_suffix; } QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) { @@ -71,7 +71,7 @@ QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* paren QStyle* style = parent->style(); QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton)); - QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent); + QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(), parent); restore_button->setObjectName(QStringLiteral("RestoreButton%1").arg(restore_button_count)); restore_button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -259,7 +259,7 @@ static void CreateIntSlider(Settings::BasicSetting& setting, bool reversed, floa QLabel* feedback, const QString& use_format, QSlider* slider, std::function& serializer, std::function& restore_func) { - int max_val = std::strtol(setting.MaxVal().c_str(), nullptr, 0); + const int max_val = std::strtol(setting.MaxVal().c_str(), nullptr, 0); const auto update_feedback = [=](int value) { int present = (reversed ? max_val - value : value) * multiplier + 0.5f; @@ -283,9 +283,10 @@ static void CreateFloatSlider(Settings::BasicSetting& setting, bool reversed, fl QLabel* feedback, const QString& use_format, QSlider* slider, std::function& serializer, std::function& restore_func) { - float max_val = std::strtof(setting.MaxVal().c_str(), nullptr); - float min_val = std::strtof(setting.MinVal().c_str(), nullptr); - float use_multiplier = multiplier == default_multiplier ? default_float_multiplier : multiplier; + const float max_val = std::strtof(setting.MaxVal().c_str(), nullptr); + const float min_val = std::strtof(setting.MinVal().c_str(), nullptr); + const float use_multiplier = + multiplier == default_multiplier ? default_float_multiplier : multiplier; const auto update_feedback = [=](float value) { int present = (reversed ? max_val - value : value) + 0.5f; @@ -330,8 +331,7 @@ QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& gi layout->setContentsMargins(0, 0, 0, 0); - QString suffix = - given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; + QString suffix = given_suffix == default_suffix ? DefaultSuffix(this, setting) : given_suffix; const QString use_format = QStringLiteral("%1").append(suffix); @@ -360,8 +360,7 @@ QWidget* Widget::CreateSpinBox(const QString& given_suffix, const auto max_val = std::strtol(setting.MaxVal().c_str(), nullptr, 0); const auto default_val = std::strtol(setting.ToString().c_str(), nullptr, 0); - QString suffix = - given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; + QString suffix = given_suffix == default_suffix ? DefaultSuffix(this, setting) : given_suffix; spinbox = new QSpinBox(this); spinbox->setRange(min_val, max_val); @@ -395,8 +394,7 @@ QWidget* Widget::CreateDoubleSpinBox(const QString& given_suffix, const auto max_val = std::strtod(setting.MaxVal().c_str(), nullptr); const auto default_val = std::strtod(setting.ToString().c_str(), nullptr); - QString suffix = - given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix; + QString suffix = given_suffix == default_suffix ? DefaultSuffix(this, setting) : given_suffix; double_spinbox = new QDoubleSpinBox(this); double_spinbox->setRange(min_val, max_val); @@ -733,10 +731,10 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati return std::pair{translations.at(id).first, translations.at(id).second}; } LOG_WARNING(Frontend, "Translation table lacks entry for \"{}\"", setting_label); - return std::pair{QString::fromStdString(setting_label), QStringLiteral("")}; + return std::pair{QString::fromStdString(setting_label), QStringLiteral()}; }(); - if (label == QStringLiteral("")) { + if (label == QStringLiteral()) { LOG_DEBUG(Frontend, "Translation table has empty entry for \"{}\", skipping...", setting.GetLabel()); return; diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h index 86edaacc88..226284cf36 100644 --- a/src/yuzu/configuration/shared_widget.h +++ b/src/yuzu/configuration/shared_widget.h @@ -44,8 +44,9 @@ enum class RequestType { MaxEnum, }; -constexpr const float default_multiplier{1.f}; -constexpr const float default_float_multiplier{100.f}; +constexpr float default_multiplier{1.f}; +constexpr float default_float_multiplier{100.f}; +static const QString default_suffix = QStringLiteral(); class Widget : public QWidget { Q_OBJECT @@ -70,8 +71,9 @@ public: const ComboboxTranslationMap& combobox_translations, QWidget* parent, bool runtime_lock, std::vector>& apply_funcs_, RequestType request = RequestType::Default, bool managed = true, - float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr, - const QString& suffix = QStringLiteral("")); + float multiplier = default_multiplier, + Settings::BasicSetting* other_setting = nullptr, + const QString& suffix = default_suffix); virtual ~Widget(); /** @@ -153,14 +155,15 @@ public: Widget* BuildWidget(Settings::BasicSetting* setting, std::vector>& apply_funcs, RequestType request = RequestType::Default, bool managed = true, - float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr, - const QString& suffix = QStringLiteral("")) const; + float multiplier = default_multiplier, + Settings::BasicSetting* other_setting = nullptr, + const QString& suffix = default_suffix) const; Widget* BuildWidget(Settings::BasicSetting* setting, std::vector>& apply_funcs, Settings::BasicSetting* other_setting, RequestType request = RequestType::Default, - const QString& suffix = QStringLiteral("")) const; + const QString& suffix = default_suffix) const; const ComboboxTranslationMap& ComboboxTranslations() const; From 21ecf01a17c9936d11f86fdb8d7bbf5c5163441c Mon Sep 17 00:00:00 2001 From: Squall-Leonhart Date: Fri, 15 Sep 2023 04:47:15 +1000 Subject: [PATCH 49/69] add std::error_code for std::filesystem exceptions Resolves a case on Windows where an unmounted bitlocker protected volume containing an assigned game directory would crash Yuzu at start. May also resolve cases where a disconnected SMB volume causes similar crashes (needs testing) --- src/common/fs/fs.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index 36e67c145c..174aed49b8 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp @@ -528,38 +528,41 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, // Generic Filesystem Operations bool Exists(const fs::path& path) { + std::error_code ec; #ifdef ANDROID if (Android::IsContentUri(path)) { return Android::Exists(path); } else { - return fs::exists(path); + return fs::exists(path, ec); } #else - return fs::exists(path); + return fs::exists(path, ec); #endif } bool IsFile(const fs::path& path) { + std::error_code ec; #ifdef ANDROID if (Android::IsContentUri(path)) { return !Android::IsDirectory(path); } else { - return fs::is_regular_file(path); + return fs::is_regular_file(path, ec); } #else - return fs::is_regular_file(path); + return fs::is_regular_file(path, ec); #endif } bool IsDir(const fs::path& path) { + std::error_code ec; #ifdef ANDROID if (Android::IsContentUri(path)) { return Android::IsDirectory(path); } else { - return fs::is_directory(path); + return fs::is_directory(path, ec); } #else - return fs::is_directory(path); + return fs::is_directory(path, ec); #endif } From 4a3cbf00216918a53b143f6bbfc4b22a49761770 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 31 Aug 2023 13:32:48 -0400 Subject: [PATCH 50/69] android: Use StateFlow instead of LiveData --- .../yuzu_emu/adapters/HomeSettingAdapter.kt | 10 +- .../features/settings/ui/SettingsActivity.kt | 53 ++++++--- .../features/settings/ui/SettingsFragment.kt | 43 ++++--- .../yuzu_emu/fragments/EmulationFragment.kt | 110 +++++++++++------- .../IndeterminateProgressDialogFragment.kt | 32 +++-- .../yuzu/yuzu_emu/fragments/SearchFragment.kt | 46 +++++--- .../fragments/SettingsSearchFragment.kt | 16 ++- .../yuzu/yuzu_emu/fragments/SetupFragment.kt | 16 ++- .../yuzu/yuzu_emu/model/EmulationViewModel.kt | 44 ++++--- .../org/yuzu/yuzu_emu/model/GamesViewModel.kt | 44 +++---- .../org/yuzu/yuzu_emu/model/HomeSetting.kt | 6 +- .../org/yuzu/yuzu_emu/model/HomeViewModel.kt | 28 ++--- .../yuzu/yuzu_emu/model/SettingsViewModel.kt | 57 ++++----- .../org/yuzu/yuzu_emu/model/TaskViewModel.kt | 27 ++--- .../org/yuzu/yuzu_emu/ui/GamesFragment.kt | 66 +++++++---- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 20 +++- 16 files changed, 368 insertions(+), 250 deletions(-) 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 8d87d3bd72..1675627a10 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/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index 908c01265c..4d2f2f604f 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 bc319714c7..2a816183ab 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 @@ -13,11 +13,15 @@ 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.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile @@ -51,6 +55,8 @@ 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( @@ -75,24 +81,27 @@ 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) + } + } } } 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 944ae652e4..1addb23265 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 @@ -129,6 +130,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) @@ -205,59 +208,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 == emulationViewModel.totalShaders.value!!) { - binding.loadingText.setText(R.string.loading) - binding.loadingProgressIndicator.isIndeterminate = true + if (it < binding.loadingProgressIndicator.max) { + binding.loadingProgressIndicator.progress = it + } + } + + 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 +298,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/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt index 181bd983af..ea8eb073ac 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 f54dccc69d..2dbca76a59 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 = 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 55b6a0367b..9d0594c6ed 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 d50c421a01..fbb2f6e18d 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 e35f51bc3a..f34870c2d9 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 get() = _emulationStarted + val emulationStarted: StateFlow get() = _emulationStarted + private val _emulationStarted = MutableStateFlow(false) - private val _isEmulationStopping = MutableLiveData(false) - val isEmulationStopping: LiveData get() = _isEmulationStopping + val isEmulationStopping: StateFlow get() = _isEmulationStopping + private val _isEmulationStopping = MutableStateFlow(false) - private val _shaderProgress = MutableLiveData(0) - val shaderProgress: LiveData get() = _shaderProgress + val shaderProgress: StateFlow get() = _shaderProgress + private val _shaderProgress = MutableStateFlow(0) - private val _totalShaders = MutableLiveData(0) - val totalShaders: LiveData get() = _totalShaders + val totalShaders: StateFlow get() = _totalShaders + private val _totalShaders = MutableStateFlow(0) - private val _shaderMessage = MutableLiveData("") - val shaderMessage: LiveData get() = _shaderMessage + val shaderMessage: StateFlow 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 1fe42f9229..6e09fa81d2 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>(emptyList()) - val games: LiveData> get() = _games + val games: StateFlow> get() = _games + private val _games = MutableStateFlow(emptyList()) - private val _searchedGames = MutableLiveData>(emptyList()) - val searchedGames: LiveData> get() = _searchedGames + val searchedGames: StateFlow> get() = _searchedGames + private val _searchedGames = MutableStateFlow(emptyList()) - private val _isReloading = MutableLiveData(false) - val isReloading: LiveData get() = _isReloading + val isReloading: StateFlow get() = _isReloading + private val _isReloading = MutableStateFlow(false) - private val _shouldSwapData = MutableLiveData(false) - val shouldSwapData: LiveData get() = _shouldSwapData + val shouldSwapData: StateFlow get() = _shouldSwapData + private val _shouldSwapData = MutableStateFlow(false) - private val _shouldScrollToTop = MutableLiveData(false) - val shouldScrollToTop: LiveData get() = _shouldScrollToTop + val shouldScrollToTop: StateFlow get() = _shouldScrollToTop + private val _shouldScrollToTop = MutableStateFlow(false) - private val _searchFocused = MutableLiveData(false) - val searchFocused: LiveData get() = _searchFocused + val searchFocused: StateFlow 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) { - _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 498c222fa9..b32e193738 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 = MutableLiveData("") + val details: StateFlow = 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 a48ef7a88a..756f767216 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>() - val navigationVisible: LiveData> get() = _navigationVisible + val navigationVisible: StateFlow> get() = _navigationVisible + private val _navigationVisible = MutableStateFlow(Pair(false, false)) - private val _statusBarShadeVisible = MutableLiveData(true) - val statusBarShadeVisible: LiveData get() = _statusBarShadeVisible + val statusBarShadeVisible: StateFlow get() = _statusBarShadeVisible + private val _statusBarShadeVisible = MutableStateFlow(true) - private val _shouldPageForward = MutableLiveData(false) - val shouldPageForward: LiveData get() = _shouldPageForward + val shouldPageForward: StateFlow get() = _shouldPageForward + private val _shouldPageForward = MutableStateFlow(false) - private val _gamesDir = MutableLiveData( + val gamesDir: StateFlow get() = _gamesDir + private val _gamesDir = MutableStateFlow( Uri.parse( PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) .getString(GameHelper.KEY_GAME_PATH, "") ).path ?: "" ) - val gamesDir: LiveData 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 d16d15fa6a..53fa7a8dec 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 get() = _toolbarTitle + val shouldRecreate: StateFlow get() = _shouldRecreate + private val _shouldRecreate = MutableStateFlow(false) - private val _shouldRecreate = MutableLiveData(false) - val shouldRecreate: LiveData get() = _shouldRecreate + val shouldNavigateBack: StateFlow get() = _shouldNavigateBack + private val _shouldNavigateBack = MutableStateFlow(false) - private val _shouldNavigateBack = MutableLiveData(false) - val shouldNavigateBack: LiveData get() = _shouldNavigateBack + val shouldShowResetSettingsDialog: StateFlow get() = _shouldShowResetSettingsDialog + private val _shouldShowResetSettingsDialog = MutableStateFlow(false) - private val _shouldShowResetSettingsDialog = MutableLiveData(false) - val shouldShowResetSettingsDialog: LiveData get() = _shouldShowResetSettingsDialog + val shouldReloadSettingsList: StateFlow get() = _shouldReloadSettingsList + private val _shouldReloadSettingsList = MutableStateFlow(false) - private val _shouldReloadSettingsList = MutableLiveData(false) - val shouldReloadSettingsList: LiveData get() = _shouldReloadSettingsList + val isUsingSearch: StateFlow get() = _isUsingSearch + private val _isUsingSearch = MutableStateFlow(false) - private val _isUsingSearch = MutableLiveData(false) - val isUsingSearch: LiveData get() = _isUsingSearch + val sliderProgress: StateFlow get() = _sliderProgress + private val _sliderProgress = MutableStateFlow(-1) - val sliderProgress = savedStateHandle.getStateFlow(KEY_SLIDER_PROGRESS, -1) + val sliderTextValue: StateFlow 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 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 27ea725a5c..531c2aaf0b 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() - val result: LiveData = _result + val result: StateFlow get() = _result + private val _result = MutableStateFlow(Any()) - private val _isComplete = MutableLiveData() - val isComplete: LiveData = _isComplete + val isComplete: StateFlow get() = _isComplete + private val _isComplete = MutableStateFlow(false) - private val _isRunning = MutableLiveData() - val isRunning: LiveData = _isRunning + val isRunning: StateFlow 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 b0156dca5e..35e3654580 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 - } - games.observe(viewLifecycleOwner) { - (binding.gridGames.adapter as GameAdapter).submitList(it) - if (it.isEmpty()) { - binding.noticeText.visibility = View.VISIBLE - } else { - binding.noticeText.visibility = View.GONE + viewLifecycleOwner.lifecycleScope.apply { + launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it } } } - shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData -> - if (shouldSwapData) { - (binding.gridGames.adapter as GameAdapter).submitList( - gamesViewModel.games.value!! - ) - gamesViewModel.setShouldSwapData(false) + launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + gamesViewModel.games.collect { + (binding.gridGames.adapter as GameAdapter).submitList(it) + if (it.isEmpty()) { + binding.noticeText.visibility = View.VISIBLE + } else { + binding.noticeText.visibility = View.GONE + } + } } } - - // 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.CREATED) { + gamesViewModel.shouldSwapData.collect { + if (it) { + (binding.gridGames.adapter as GameAdapter).submitList( + gamesViewModel.games.value + ) + gamesViewModel.setShouldSwapData(false) + } + } + } + } + launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + 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 7d8e06ad8a..1ee833cf63 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 @@ -115,16 +117,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) From 8baed5d95d27c9947b1bf3ef6cb39c4344f47486 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 14 Sep 2023 15:05:44 -0400 Subject: [PATCH 51/69] android: Refactor menu tags to enum --- .../features/settings/model/Settings.kt | 11 +++++ .../settings/model/view/SubmenuSetting.kt | 4 +- .../features/settings/ui/SettingsFragment.kt | 10 +++-- .../settings/ui/SettingsFragmentPresenter.kt | 45 ++++++------------- .../yuzu_emu/fragments/EmulationFragment.kt | 3 +- .../fragments/HomeSettingsFragment.kt | 5 +-- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 3 +- .../main/res/navigation/home_navigation.xml | 2 +- .../res/navigation/settings_navigation.xml | 2 +- 9 files changed, 39 insertions(+), 46 deletions(-) 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 0702236e81..08e2a973db 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 91c273964a..b343e527ef 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/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt index 2a816183ab..70d8ec14bb 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 @@ -21,10 +22,11 @@ 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() { @@ -62,10 +64,10 @@ class SettingsFragment : Fragment() { presenter = SettingsFragmentPresenter( settingsViewModel, settingsAdapter!!, - args.menuTag, - args.game?.gameId ?: "" + args.menuTag ) + binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) val dividerDecoration = MaterialDividerItemDecoration( requireContext(), LinearLayoutManager.VERTICAL @@ -105,7 +107,7 @@ class SettingsFragment : Fragment() { } } - 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 22a529b1ba..766414a6c8 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() @@ -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() 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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 1addb23265..3e6c157c7f 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 @@ -50,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 @@ -166,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 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 cbbe14d220..c119e69c97 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/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 1ee833cf63..b6b6c6c171 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 @@ -42,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 @@ -109,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) } 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 2085430bf8..2e0ce7a3dc 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" /> + app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" /> + app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" /> Date: Thu, 14 Sep 2023 14:34:05 -0400 Subject: [PATCH 52/69] core: improve debug workflow --- src/core/file_sys/patch_manager.cpp | 4 ++-- src/core/file_sys/patch_manager.h | 2 +- src/core/hle/kernel/k_process.cpp | 5 ++++- src/core/hle/kernel/k_process.h | 8 +++++++- src/core/hle/kernel/svc/svc_debug_string.cpp | 2 +- src/core/hle/kernel/svc/svc_exception.cpp | 6 +++++- src/core/loader/deconstructed_rom_directory.cpp | 8 ++++---- src/core/loader/deconstructed_rom_directory.h | 4 +++- src/core/loader/kip.cpp | 3 ++- src/core/loader/nro.cpp | 3 ++- src/core/loader/nso.cpp | 5 +++-- src/core/loader/nsp.cpp | 3 ++- 12 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index a4baddb15a..8e475f25a3 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -294,11 +294,11 @@ std::vector PatchManager::PatchNSO(const std::vector& nso, const std::st return out; } -bool PatchManager::HasNSOPatch(const BuildID& build_id_) const { +bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name) const { const auto build_id_raw = Common::HexToString(build_id_); const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); - LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); + LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name); const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); if (load_dir == nullptr) { diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index adcde7b7d6..03e9c7301d 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -52,7 +52,7 @@ public: // Checks to see if PatchNSO() will have any effect given the NSO's build ID. // Used to prevent expensive copies in NSO loader. - [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const; + [[nodiscard]] bool HasNSOPatch(const BuildID& build_id, std::string_view name) const; // Creates a CheatList object with all [[nodiscard]] std::vector CreateCheatList( diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 703049ede9..4a099286b5 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -96,6 +96,7 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process->m_is_suspended = false; process->m_schedule_count = 0; process->m_is_handle_table_initialized = false; + process->m_is_hbl = false; // Open a reference to the resource limit. process->m_resource_limit->Open(); @@ -351,12 +352,14 @@ Result KProcess::SetActivity(ProcessActivity activity) { R_SUCCEED(); } -Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) { +Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, + bool is_hbl) { m_program_id = metadata.GetTitleID(); m_ideal_core = metadata.GetMainThreadCore(); m_is_64bit_process = metadata.Is64BitProgram(); m_system_resource_size = metadata.GetSystemResourceSize(); m_image_size = code_size; + m_is_hbl = is_hbl; if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit) { // For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large. diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 4fdeaf11a0..146e07a57b 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -338,7 +338,8 @@ public: * @returns ResultSuccess if all relevant metadata was able to be * loaded and parsed. Otherwise, an error code is returned. */ - Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size); + Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, + bool is_hbl); /** * Starts the main application thread for this process. @@ -368,6 +369,10 @@ public: return GetProcessId(); } + bool IsHbl() const { + return m_is_hbl; + } + bool IsSignaled() const override; void DoWorkerTaskImpl(); @@ -525,6 +530,7 @@ private: bool m_is_immortal{}; bool m_is_handle_table_initialized{}; bool m_is_initialized{}; + bool m_is_hbl{}; std::atomic m_num_running_threads{}; diff --git a/src/core/hle/kernel/svc/svc_debug_string.cpp b/src/core/hle/kernel/svc/svc_debug_string.cpp index 4c14ce6683..00b65429be 100644 --- a/src/core/hle/kernel/svc/svc_debug_string.cpp +++ b/src/core/hle/kernel/svc/svc_debug_string.cpp @@ -14,7 +14,7 @@ Result OutputDebugString(Core::System& system, u64 address, u64 len) { std::string str(len, '\0'); GetCurrentMemory(system.Kernel()).ReadBlock(address, str.data(), str.size()); - LOG_DEBUG(Debug_Emulated, "{}", str); + LOG_INFO(Debug_Emulated, "{}", str); R_SUCCEED(); } diff --git a/src/core/hle/kernel/svc/svc_exception.cpp b/src/core/hle/kernel/svc/svc_exception.cpp index 580cf2f75c..c581c086b8 100644 --- a/src/core/hle/kernel/svc/svc_exception.cpp +++ b/src/core/hle/kernel/svc/svc_exception.cpp @@ -3,6 +3,7 @@ #include "core/core.h" #include "core/debugger/debugger.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc_types.h" @@ -107,7 +108,10 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) { system.ArmInterface(static_cast(thread_processor_id)).LogBacktrace(); } - if (system.DebuggerEnabled()) { + const bool is_hbl = GetCurrentProcess(system.Kernel()).IsHbl(); + const bool should_break = is_hbl || !notification_only; + + if (system.DebuggerEnabled() && should_break) { auto* thread = system.Kernel().GetCurrentEmuThread(); system.GetDebugger().NotifyThreadStopped(thread); thread->RequestSuspend(Kernel::SuspendType::Debug); diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index f4eaf3331f..5a42dea486 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -18,7 +18,7 @@ namespace Loader { AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, bool override_update_) - : AppLoader(std::move(file_)), override_update(override_update_) { + : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { const auto file_dir = file->GetContainingDirectory(); // Title ID @@ -69,9 +69,9 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys } AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( - FileSys::VirtualDir directory, bool override_update_) + FileSys::VirtualDir directory, bool override_update_, bool is_hbl_) : AppLoader(directory->GetFile("main")), dir(std::move(directory)), - override_update(override_update_) {} + override_update(override_update_), is_hbl(is_hbl_) {} FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) { if (FileSys::IsDirectoryExeFS(dir_file->GetContainingDirectory())) { @@ -147,7 +147,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } // Setup the process code layout - if (process.LoadFromMetadata(metadata, code_size).IsError()) { + if (process.LoadFromMetadata(metadata, code_size, is_hbl).IsError()) { return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; } diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index f7702225ef..1e9f765c9d 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -27,7 +27,8 @@ public: // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory, - bool override_update_ = false); + bool override_update_ = false, + bool is_hbl_ = false); /** * Identifies whether or not the given file is a deconstructed ROM directory. @@ -62,6 +63,7 @@ private: std::string name; u64 title_id{}; bool override_update; + bool is_hbl; Modules modules; }; diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index d722459c64..bf56a08b40 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -90,7 +90,8 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process, codeset.DataSegment().size += kip->GetBSSSize(); // Setup the process code layout - if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size()) + if (process + .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) .IsError()) { return {ResultStatus::ErrorNotInitialized, {}}; } diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index d7562b4bc1..69f1a54ed6 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -196,7 +196,8 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector& data) program_image.resize(static_cast(program_image.size()) + bss_size); // Setup the process code layout - if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size()) + if (process + .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) .IsError()) { return false; } diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 549822506b..1350da8dca 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -127,13 +127,14 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: } // Apply patches if necessary - if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { + const auto name = nso_file.GetName(); + if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { std::vector pi_header(sizeof(NSOHeader) + program_image.size()); std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(), program_image.size()); - pi_header = pm->PatchNSO(pi_header, nso_file.GetName()); + pi_header = pm->PatchNSO(pi_header, name); std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data()); } diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index fe2af1ae6f..f4ab75b770 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -30,7 +30,8 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, } if (nsp->IsExtractedType()) { - secondary_loader = std::make_unique(nsp->GetExeFS()); + secondary_loader = std::make_unique( + nsp->GetExeFS(), false, file->GetName() == "hbl.nsp"); } else { const auto control_nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); From b39438917015430bdefe21f352f41bcad2d9d397 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 14 Sep 2023 20:02:48 -0400 Subject: [PATCH 53/69] android: Delay collecting UI state in games fragment --- .../src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 35e3654580..805b89b31d 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 @@ -93,12 +93,12 @@ class GamesFragment : Fragment() { viewLifecycleOwner.lifecycleScope.apply { launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { + repeatOnLifecycle(Lifecycle.State.RESUMED) { gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it } } } launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { + repeatOnLifecycle(Lifecycle.State.RESUMED) { gamesViewModel.games.collect { (binding.gridGames.adapter as GameAdapter).submitList(it) if (it.isEmpty()) { @@ -110,7 +110,7 @@ class GamesFragment : Fragment() { } } launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { + repeatOnLifecycle(Lifecycle.State.RESUMED) { gamesViewModel.shouldSwapData.collect { if (it) { (binding.gridGames.adapter as GameAdapter).submitList( @@ -122,7 +122,7 @@ class GamesFragment : Fragment() { } } launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { + repeatOnLifecycle(Lifecycle.State.RESUMED) { gamesViewModel.shouldScrollToTop.collect { if (it) { scrollToTop() From 21b133de4012755f95c450cb52c1cb9d40b2a730 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 14 Sep 2023 20:24:43 -0400 Subject: [PATCH 54/69] android: Fix emulation to settings navigation args --- .../app/src/main/res/navigation/emulation_navigation.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c7be37f9b2..cfc494b3f1 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" /> + app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" /> Date: Thu, 14 Sep 2023 21:25:17 -0400 Subject: [PATCH 55/69] android: Don't reinitialize settings on emulation start Config is already initialized on application start --- src/android/app/src/main/jni/native.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index b9ecefa745..8ac28b6386 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -262,9 +262,6 @@ 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(&m_input_subsystem, m_native_window, m_vulkan_library); From 6481f4e9371c9cd45878d06f8b9471883d81fd90 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 14 Sep 2023 21:16:07 -0400 Subject: [PATCH 56/69] android: Use resource as shortcut intermediary Fixes issue where the shortcut icon would appear cropped on certain devices --- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 23 ++++++++++++++++--- .../org/yuzu/yuzu_emu/utils/GameIconUtils.kt | 11 +++++++++ .../app/src/main/res/drawable/shortcut.xml | 11 +++++++++ .../app/src/main/res/values/dimens.xml | 1 + 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/android/app/src/main/res/drawable/shortcut.xml 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 0013e8512d..f9f88a1d29 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/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt index c0fe596d7d..9fe99fab19 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/res/drawable/shortcut.xml b/src/android/app/src/main/res/drawable/shortcut.xml new file mode 100644 index 0000000000..c749e5d729 --- /dev/null +++ b/src/android/app/src/main/res/drawable/shortcut.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml index 00757e5e8e..7b2296d956 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 @@ 72dp 256dp 165dp + 24dp 20dp 3dp From 7e2bd395bc1ffe986a3965dc2872e4c87e63ce58 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 14 Sep 2023 23:03:19 -0400 Subject: [PATCH 57/69] android: Return the correct status code on emulation stop --- src/android/app/src/main/jni/native.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 8ac28b6386..8a2021ff06 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -327,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() { From 832a2fcc6923164e16480400c6dbb8803b0f5521 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 14 Sep 2023 23:46:19 -0400 Subject: [PATCH 58/69] android: Remove settings interface specifically for audio mute --- .../main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 15 --------------- .../yuzu/yuzu_emu/activities/EmulationActivity.kt | 8 ++++---- .../features/settings/model/BooleanSetting.kt | 1 + src/android/app/src/main/jni/native.cpp | 12 ------------ 4 files changed, 5 insertions(+), 31 deletions(-) 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 c8706d7a63..21f67f32a2 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 @@ -307,21 +307,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 */ 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 bbd328c717..d4ae39661f 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/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index e0c0538c78..8476ce8671 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/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 8a2021ff06..f31fe054b6 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -670,18 +670,6 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass claz return static_cast(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(Settings::values.audio_muted.GetValue()); -} - jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) { return EmulationSession::GetInstance().IsHandheldOnly(); } From 0d4aa9125e3cf457b970fd33b05b5255d44a59d3 Mon Sep 17 00:00:00 2001 From: german77 Date: Fri, 15 Sep 2023 21:41:05 -0600 Subject: [PATCH 59/69] service: nfc: Fix amiibo formatting --- src/core/hle/service/nfc/common/device.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 5dda123438..674d2e4b25 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -874,17 +874,19 @@ Result NfcDevice::RestoreAmiibo() { } Result NfcDevice::Format() { - auto result1 = DeleteApplicationArea(); - auto result2 = DeleteRegisterInfo(); + Result result = ResultSuccess; - if (result1.IsError()) { - return result1; + if (device_state == DeviceState::TagFound) { + result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All); } - if (result2.IsError()) { - return result2; + if (result.IsError()) { + return result; } + DeleteApplicationArea(); + DeleteRegisterInfo(); + return Flush(); } From c8b9467f502a37abf2198df556b03510f104732a Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 16 Sep 2023 00:17:30 -0600 Subject: [PATCH 60/69] service: hid: Ensure state is correct --- src/core/hle/service/hid/controllers/npad.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 3b349b4c4e..a894af8ea5 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -419,9 +419,17 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { std::scoped_lock lock{mutex}; auto& controller = GetControllerFromNpadIdType(npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); + + if (!controller.device->IsConnected() && controller.is_connected) { + DisconnectNpad(npad_id); + return; + } if (!controller.device->IsConnected()) { return; } + if (controller.device->IsConnected() && !controller.is_connected) { + InitNewlyAddedController(npad_id); + } // This function is unique to yuzu for the turbo buttons and motion to work properly controller.device->StatusUpdate(); @@ -736,14 +744,6 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { // Once SetSupportedStyleSet is called controllers are fully initialized is_controller_initialized = true; - - // Connect all active controllers - for (auto& controller : controller_data) { - const auto& device = controller.device; - if (device->IsConnected()) { - AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); - } - } } Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { From 8950fe79ad0f1a10880556856eca9f987cdfd886 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 16 Sep 2023 00:21:47 -0600 Subject: [PATCH 61/69] hid: service: Implement Last active Npad --- src/core/hid/hid_core.cpp | 8 ++++++++ src/core/hid/hid_core.h | 7 +++++++ src/core/hle/service/hid/controllers/npad.cpp | 4 ++++ src/core/hle/service/hid/hid.cpp | 2 +- 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp index 7d63734144..cf53c04d9a 100644 --- a/src/core/hid/hid_core.cpp +++ b/src/core/hid/hid_core.cpp @@ -154,6 +154,14 @@ NpadIdType HIDCore::GetFirstDisconnectedNpadId() const { return NpadIdType::Player1; } +void HIDCore::SetLastActiveController(NpadIdType npad_id) { + last_active_controller = npad_id; +} + +NpadIdType HIDCore::GetLastActiveController() const { + return last_active_controller; +} + void HIDCore::EnableAllControllerConfiguration() { player_1->EnableConfiguration(); player_2->EnableConfiguration(); diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h index 5fe36551ea..80abab18bb 100644 --- a/src/core/hid/hid_core.h +++ b/src/core/hid/hid_core.h @@ -48,6 +48,12 @@ public: /// Returns the first disconnected npad id NpadIdType GetFirstDisconnectedNpadId() const; + /// Sets the npad id of the last active controller + void SetLastActiveController(NpadIdType npad_id); + + /// Returns the npad id of the last controller that pushed a button + NpadIdType GetLastActiveController() const; + /// Sets all emulated controllers into configuring mode. void EnableAllControllerConfiguration(); @@ -77,6 +83,7 @@ private: std::unique_ptr console; std::unique_ptr devices; NpadStyleTag supported_style_tag{NpadStyleSet::All}; + NpadIdType last_active_controller{NpadIdType::Handheld}; }; } // namespace Core::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index a894af8ea5..da3220ddcc 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -476,6 +476,10 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { pad_entry.npad_buttons.l.Assign(button_state.zl); pad_entry.npad_buttons.r.Assign(button_state.zr); } + + if (pad_entry.npad_buttons.raw != Core::HID::NpadButton::None) { + hid_core.SetLastActiveController(npad_id); + } } void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index fd466db7b4..3657e61d35 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2768,7 +2768,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(Core::HID::NpadIdType::Handheld); + rb.PushEnum(system.HIDCore().GetLastActiveController()); } void GetUniquePadsFromNpad(HLERequestContext& ctx) { From 260bfc4bd245e8025c08922b6910039e9e6d242d Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 16 Sep 2023 00:22:52 -0600 Subject: [PATCH 62/69] hid: service: Remove outdated field from npad --- src/core/hle/service/hid/controllers/npad.cpp | 21 +++++++++---------- src/core/hle/service/hid/controllers/npad.h | 20 ++++-------------- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index da3220ddcc..e7d25e86ea 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -193,7 +193,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { shared_memory->system_properties.use_minus.Assign(1); shared_memory->system_properties.is_charging_joy_dual.Assign( battery_level.dual.is_charging); - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; + shared_memory->applet_footer_type = AppletFooterUiType::SwitchProController; shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::Handheld: @@ -216,8 +216,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { shared_memory->system_properties.is_charging_joy_right.Assign( battery_level.right.is_charging); shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; - shared_memory->applet_nfc_xcd.applet_footer.type = - AppletFooterUiType::HandheldJoyConLeftJoyConRight; + shared_memory->applet_footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconDual: @@ -247,19 +246,19 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; if (controller.is_dual_left_connected && controller.is_dual_right_connected) { - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual; + shared_memory->applet_footer_type = AppletFooterUiType::JoyDual; shared_memory->fullkey_color.fullkey = body_colors.left; shared_memory->battery_level_dual = battery_level.left.battery_level; shared_memory->system_properties.is_charging_joy_dual.Assign( battery_level.left.is_charging); } else if (controller.is_dual_left_connected) { - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; + shared_memory->applet_footer_type = AppletFooterUiType::JoyDualLeftOnly; shared_memory->fullkey_color.fullkey = body_colors.left; shared_memory->battery_level_dual = battery_level.left.battery_level; shared_memory->system_properties.is_charging_joy_dual.Assign( battery_level.left.is_charging); } else { - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; + shared_memory->applet_footer_type = AppletFooterUiType::JoyDualRightOnly; shared_memory->fullkey_color.fullkey = body_colors.right; shared_memory->battery_level_dual = battery_level.right.battery_level; shared_memory->system_properties.is_charging_joy_dual.Assign( @@ -278,7 +277,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { shared_memory->system_properties.use_minus.Assign(1); shared_memory->system_properties.is_charging_joy_left.Assign( battery_level.left.is_charging); - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; + shared_memory->applet_footer_type = AppletFooterUiType::JoyLeftHorizontal; shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconRight: @@ -293,7 +292,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { shared_memory->system_properties.use_plus.Assign(1); shared_memory->system_properties.is_charging_joy_right.Assign( battery_level.right.is_charging); - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; + shared_memory->applet_footer_type = AppletFooterUiType::JoyRightHorizontal; shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::GameCube: @@ -314,12 +313,12 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { case Core::HID::NpadStyleIndex::SNES: shared_memory->style_tag.lucia.Assign(1); shared_memory->device_type.fullkey.Assign(1); - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lucia; + shared_memory->applet_footer_type = AppletFooterUiType::Lucia; break; case Core::HID::NpadStyleIndex::N64: shared_memory->style_tag.lagoon.Assign(1); shared_memory->device_type.fullkey.Assign(1); - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lagon; + shared_memory->applet_footer_type = AppletFooterUiType::Lagon; break; case Core::HID::NpadStyleIndex::SegaGenesis: shared_memory->style_tag.lager.Assign(1); @@ -1120,7 +1119,7 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { .left = {}, .right = {}, }; - shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::None; + shared_memory->applet_footer_type = AppletFooterUiType::None; controller.is_dual_left_connected = true; controller.is_dual_right_connected = true; diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 776411261d..09707d2f84 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -360,7 +360,7 @@ private: enum class AppletFooterUiType : u8 { None = 0, HandheldNone = 1, - HandheldJoyConLeftOnly = 1, + HandheldJoyConLeftOnly = 2, HandheldJoyConRightOnly = 3, HandheldJoyConLeftJoyConRight = 4, JoyDual = 5, @@ -382,13 +382,6 @@ private: Lagon = 21, }; - struct AppletFooterUi { - AppletFooterUiAttributes attributes{}; - AppletFooterUiType type{AppletFooterUiType::None}; - INSERT_PADDING_BYTES(0x5B); // Reserved - }; - static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); - // This is nn::hid::NpadLarkType enum class NpadLarkType : u32 { Invalid, @@ -419,13 +412,6 @@ private: U, }; - struct AppletNfcXcd { - union { - AppletFooterUi applet_footer{}; - Lifo nfc_xcd_device_lifo; - }; - }; - // This is nn::hid::detail::NpadInternalState struct NpadInternalState { Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; @@ -452,7 +438,9 @@ private: Core::HID::NpadBatteryLevel battery_level_dual{}; Core::HID::NpadBatteryLevel battery_level_left{}; Core::HID::NpadBatteryLevel battery_level_right{}; - AppletNfcXcd applet_nfc_xcd{}; + AppletFooterUiAttributes applet_footer_attributes{}; + AppletFooterUiType applet_footer_type{AppletFooterUiType::None}; + INSERT_PADDING_BYTES(0x5B); // Reserved INSERT_PADDING_BYTES(0x20); // Unknown Lifo gc_trigger_lifo{}; NpadLarkType lark_type_l_and_main{}; From a3f235f8a23303796a3727978806f2dcb13894eb Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 16 Sep 2023 08:38:10 -0600 Subject: [PATCH 63/69] service: hid: Implement ApplyNpadSystemCommonPolicy --- src/core/hle/service/hid/controllers/npad.cpp | 25 +++++++++++++++ src/core/hle/service/hid/controllers/npad.h | 2 ++ src/core/hle/service/hid/hid.cpp | 31 ++++++++++++++----- src/core/hle/service/hid/hid.h | 2 +- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 3b349b4c4e..c744b0f877 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -1508,6 +1508,31 @@ Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { return static_cast(press_state.exchange(0)); } +void Controller_NPad::ApplyNpadSystemCommonPolicy() { + Core::HID::NpadStyleTag styletag{}; + styletag.fullkey.Assign(1); + styletag.handheld.Assign(1); + styletag.joycon_dual.Assign(1); + styletag.system_ext.Assign(1); + styletag.system.Assign(1); + SetSupportedStyleSet(styletag); + + SetNpadHandheldActivationMode(NpadHandheldActivationMode::Dual); + + supported_npad_id_types.clear(); + supported_npad_id_types.resize(10); + supported_npad_id_types[0] = Core::HID::NpadIdType::Player1; + supported_npad_id_types[1] = Core::HID::NpadIdType::Player2; + supported_npad_id_types[2] = Core::HID::NpadIdType::Player3; + supported_npad_id_types[3] = Core::HID::NpadIdType::Player4; + supported_npad_id_types[4] = Core::HID::NpadIdType::Player5; + supported_npad_id_types[5] = Core::HID::NpadIdType::Player6; + supported_npad_id_types[6] = Core::HID::NpadIdType::Player7; + supported_npad_id_types[7] = Core::HID::NpadIdType::Player8; + supported_npad_id_types[8] = Core::HID::NpadIdType::Other; + supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; +} + bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { if (controller == Core::HID::NpadStyleIndex::Handheld) { const bool support_handheld = diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 776411261d..0e9f899a45 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -190,6 +190,8 @@ public: // Specifically for cheat engine and other features. Core::HID::NpadButton GetAndResetPressState(); + void ApplyNpadSystemCommonPolicy(); + static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); static Result VerifyValidSixAxisSensorHandle( diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index fd466db7b4..6f711c0da7 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -231,8 +231,10 @@ std::shared_ptr Hid::GetAppletResource() { return applet_resource; } -Hid::Hid(Core::System& system_) - : ServiceFramework{system_, "hid"}, service_context{system_, service_name} { +Hid::Hid(Core::System& system_, std::shared_ptr applet_resource_) + : ServiceFramework{system_, "hid"}, applet_resource{applet_resource_}, service_context{ + system_, + service_name} { // clang-format off static const FunctionInfo functions[] = { {0, &Hid::CreateAppletResource, "CreateAppletResource"}, @@ -2543,8 +2545,9 @@ public: class HidSys final : public ServiceFramework { public: - explicit HidSys(Core::System& system_) - : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"} { + explicit HidSys(Core::System& system_, std::shared_ptr applet_resource_) + : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"}, + applet_resource{applet_resource_} { // clang-format off static const FunctionInfo functions[] = { {31, nullptr, "SendKeyboardLockKeyEvent"}, @@ -2756,9 +2759,12 @@ public: private: void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { - // We already do this for homebrew so we can just stub it out LOG_WARNING(Service_HID, "called"); + GetAppletResource() + ->GetController(HidController::NPad) + .ApplyNpadSystemCommonPolicy(); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -2821,17 +2827,28 @@ private: rb.PushRaw(touchscreen_config); } + std::shared_ptr GetAppletResource() { + if (applet_resource == nullptr) { + applet_resource = std::make_shared(system, service_context); + } + + return applet_resource; + } + Kernel::KEvent* joy_detach_event; KernelHelpers::ServiceContext service_context; + std::shared_ptr applet_resource; }; void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); + std::shared_ptr applet_resource; - server_manager->RegisterNamedService("hid", std::make_shared(system)); + server_manager->RegisterNamedService("hid", std::make_shared(system, applet_resource)); server_manager->RegisterNamedService("hidbus", std::make_shared(system)); server_manager->RegisterNamedService("hid:dbg", std::make_shared(system)); - server_manager->RegisterNamedService("hid:sys", std::make_shared(system)); + server_manager->RegisterNamedService("hid:sys", + std::make_shared(system, applet_resource)); server_manager->RegisterNamedService("irs", std::make_shared(system)); server_manager->RegisterNamedService("irs:sys", diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index f247b83c25..0ca43de935 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -95,7 +95,7 @@ private: class Hid final : public ServiceFramework { public: - explicit Hid(Core::System& system_); + explicit Hid(Core::System& system_, std::shared_ptr applet_resource_); ~Hid() override; std::shared_ptr GetAppletResource(); From 67e2d5c28b8423c4f3f1d5b00f87325684158a6f Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Thu, 31 Aug 2023 15:09:15 +0100 Subject: [PATCH 64/69] Reimplement HardwareOpus --- src/audio_core/CMakeLists.txt | 16 +- src/audio_core/adsp/adsp.cpp | 13 +- src/audio_core/adsp/adsp.h | 3 + .../apps/audio_renderer/audio_renderer.cpp | 48 +- .../adsp/apps/audio_renderer/audio_renderer.h | 45 +- .../adsp/apps/opus/opus_decode_object.cpp | 107 +++ .../adsp/apps/opus/opus_decode_object.h | 38 + .../adsp/apps/opus/opus_decoder.cpp | 269 +++++++ src/audio_core/adsp/apps/opus/opus_decoder.h | 92 +++ .../opus/opus_multistream_decode_object.cpp | 111 +++ .../opus/opus_multistream_decode_object.h | 39 + src/audio_core/adsp/apps/opus/shared_memory.h | 17 + src/audio_core/adsp/mailbox.h | 27 +- src/audio_core/opus/decoder.cpp | 179 +++++ src/audio_core/opus/decoder.h | 53 ++ src/audio_core/opus/decoder_manager.cpp | 102 +++ src/audio_core/opus/decoder_manager.h | 38 + src/audio_core/opus/hardware_opus.cpp | 241 ++++++ src/audio_core/opus/hardware_opus.h | 45 ++ src/audio_core/opus/parameters.h | 54 ++ src/common/CMakeLists.txt | 5 +- src/common/bounded_threadsafe_queue.h | 4 +- src/core/CMakeLists.txt | 2 +- src/core/hle/result.h | 2 +- src/core/hle/service/audio/errors.h | 12 + src/core/hle/service/audio/hwopus.cpp | 754 ++++++++++-------- src/core/hle/service/audio/hwopus.h | 25 +- 27 files changed, 1914 insertions(+), 427 deletions(-) create mode 100644 src/audio_core/adsp/apps/opus/opus_decode_object.cpp create mode 100644 src/audio_core/adsp/apps/opus/opus_decode_object.h create mode 100644 src/audio_core/adsp/apps/opus/opus_decoder.cpp create mode 100644 src/audio_core/adsp/apps/opus/opus_decoder.h create mode 100644 src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp create mode 100644 src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h create mode 100644 src/audio_core/adsp/apps/opus/shared_memory.h create mode 100644 src/audio_core/opus/decoder.cpp create mode 100644 src/audio_core/opus/decoder.h create mode 100644 src/audio_core/opus/decoder_manager.cpp create mode 100644 src/audio_core/opus/decoder_manager.h create mode 100644 src/audio_core/opus/hardware_opus.cpp create mode 100644 src/audio_core/opus/hardware_opus.h create mode 100644 src/audio_core/opus/parameters.h diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 67dfe0290d..400988c5fb 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -10,6 +10,13 @@ add_library(audio_core STATIC adsp/apps/audio_renderer/command_buffer.h adsp/apps/audio_renderer/command_list_processor.cpp adsp/apps/audio_renderer/command_list_processor.h + adsp/apps/opus/opus_decoder.cpp + adsp/apps/opus/opus_decoder.h + adsp/apps/opus/opus_decode_object.cpp + adsp/apps/opus/opus_decode_object.h + adsp/apps/opus/opus_multistream_decode_object.cpp + adsp/apps/opus/opus_multistream_decode_object.h + adsp/apps/opus/shared_memory.h audio_core.cpp audio_core.h audio_event.h @@ -35,6 +42,13 @@ add_library(audio_core STATIC in/audio_in.h in/audio_in_system.cpp in/audio_in_system.h + opus/hardware_opus.cpp + opus/hardware_opus.h + opus/decoder_manager.cpp + opus/decoder_manager.h + opus/decoder.cpp + opus/decoder.h + opus/parameters.h out/audio_out.cpp out/audio_out.h out/audio_out_system.cpp @@ -214,7 +228,7 @@ else() ) endif() -target_link_libraries(audio_core PUBLIC common core) +target_link_libraries(audio_core PUBLIC common core Opus::opus) if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) endif() diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp index 0580990f5b..6c53c98fd5 100644 --- a/src/audio_core/adsp/adsp.cpp +++ b/src/audio_core/adsp/adsp.cpp @@ -7,12 +7,21 @@ namespace AudioCore::ADSP { ADSP::ADSP(Core::System& system, Sink::Sink& sink) { - audio_renderer = - std::make_unique(system, system.ApplicationMemory(), sink); + audio_renderer = std::make_unique(system, sink); + opus_decoder = std::make_unique(system); + opus_decoder->Send(Direction::DSP, OpusDecoder::Message::Start); + if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) { + LOG_ERROR(Service_Audio, "OpusDeocder failed to initialize."); + return; + } } AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { return *audio_renderer.get(); } +OpusDecoder::OpusDecoder& ADSP::OpusDecoder() { + return *opus_decoder.get(); +} + } // namespace AudioCore::ADSP diff --git a/src/audio_core/adsp/adsp.h b/src/audio_core/adsp/adsp.h index bd5bcc63ba..a0c24a16a2 100644 --- a/src/audio_core/adsp/adsp.h +++ b/src/audio_core/adsp/adsp.h @@ -4,6 +4,7 @@ #pragma once #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" +#include "audio_core/adsp/apps/opus/opus_decoder.h" #include "common/common_types.h" namespace Core { @@ -40,10 +41,12 @@ public: ~ADSP() = default; AudioRenderer::AudioRenderer& AudioRenderer(); + OpusDecoder::OpusDecoder& OpusDecoder(); private: /// AudioRenderer app std::unique_ptr audio_renderer{}; + std::unique_ptr opus_decoder{}; }; } // namespace ADSP diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp index 2e549bc6f5..972d5e45bc 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp @@ -14,13 +14,12 @@ #include "core/core.h" #include "core/core_timing.h" -MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); +MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP_AudioRenderer", MP_RGB(60, 19, 97)); namespace AudioCore::ADSP::AudioRenderer { -AudioRenderer::AudioRenderer(Core::System& system_, Core::Memory::Memory& memory_, - Sink::Sink& sink_) - : system{system_}, memory{memory_}, sink{sink_} {} +AudioRenderer::AudioRenderer(Core::System& system_, Sink::Sink& sink_) + : system{system_}, sink{sink_} {} AudioRenderer::~AudioRenderer() { Stop(); @@ -33,8 +32,8 @@ void AudioRenderer::Start() { main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); }); - mailbox.Send(Direction::DSP, {Message::InitializeOK, {}}); - if (mailbox.Receive(Direction::Host).msg != Message::InitializeOK) { + mailbox.Send(Direction::DSP, Message::InitializeOK); + if (mailbox.Receive(Direction::Host) != Message::InitializeOK) { LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " "message response from ADSP!"); return; @@ -47,8 +46,8 @@ void AudioRenderer::Stop() { return; } - mailbox.Send(Direction::DSP, {Message::Shutdown, {}}); - if (mailbox.Receive(Direction::Host).msg != Message::Shutdown) { + mailbox.Send(Direction::DSP, Message::Shutdown); + if (mailbox.Receive(Direction::Host) != Message::Shutdown) { LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " "message response from ADSP!"); } @@ -67,25 +66,25 @@ void AudioRenderer::Stop() { void AudioRenderer::Signal() { signalled_tick = system.CoreTiming().GetGlobalTimeNs().count(); - Send(Direction::DSP, {Message::Render, {}}); + Send(Direction::DSP, Message::Render); } void AudioRenderer::Wait() { - auto received = Receive(Direction::Host); - if (received.msg != Message::RenderResponse) { + auto msg = Receive(Direction::Host); + if (msg != Message::RenderResponse) { LOG_ERROR(Service_Audio, "Did not receive the expected render response from the AudioRenderer! Expected " "{}, got {}", - Message::RenderResponse, received.msg); + Message::RenderResponse, msg); } } -void AudioRenderer::Send(Direction dir, MailboxMessage message) { +void AudioRenderer::Send(Direction dir, u32 message) { mailbox.Send(dir, std::move(message)); } -MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { - return mailbox.Receive(dir, block); +u32 AudioRenderer::Receive(Direction dir) { + return mailbox.Receive(dir); } void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, @@ -120,7 +119,7 @@ void AudioRenderer::CreateSinkStreams() { } void AudioRenderer::Main(std::stop_token stop_token) { - static constexpr char name[]{"AudioRenderer"}; + static constexpr char name[]{"DSP_AudioRenderer_Main"}; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); @@ -128,28 +127,28 @@ void AudioRenderer::Main(std::stop_token stop_token) { // TODO: Create buffer map/unmap thread + mailbox // TODO: Create gMix devices, initialize them here - if (mailbox.Receive(Direction::DSP).msg != Message::InitializeOK) { + if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) { LOG_ERROR(Service_Audio, "ADSP Audio Renderer -- Failed to receive initialize message from host!"); return; } - mailbox.Send(Direction::Host, {Message::InitializeOK, {}}); + mailbox.Send(Direction::Host, Message::InitializeOK); // 0.12 seconds (2,304,000 / 19,200,000) constexpr u64 max_process_time{2'304'000ULL}; while (!stop_token.stop_requested()) { - auto received{mailbox.Receive(Direction::DSP)}; - switch (received.msg) { + auto msg{mailbox.Receive(Direction::DSP)}; + switch (msg) { case Message::Shutdown: - mailbox.Send(Direction::Host, {Message::Shutdown, {}}); + mailbox.Send(Direction::Host, Message::Shutdown); return; case Message::Render: { if (system.IsShuttingDown()) [[unlikely]] { std::this_thread::sleep_for(std::chrono::milliseconds(5)); - mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); + mailbox.Send(Direction::Host, Message::RenderResponse); continue; } std::array buffers_reset{}; @@ -205,13 +204,12 @@ void AudioRenderer::Main(std::stop_token stop_token) { } } - mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); + mailbox.Send(Direction::Host, Message::RenderResponse); } break; default: LOG_WARNING(Service_Audio, - "ADSP AudioRenderer received an invalid message, msg={:02X}!", - received.msg); + "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg); break; } } diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h index 3f5b7dca2a..85874d88ac 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h @@ -17,13 +17,6 @@ namespace Core { class System; -namespace Timing { -struct EventType; -} -namespace Memory { -class Memory; -} -class System; } // namespace Core namespace AudioCore { @@ -34,19 +27,19 @@ class Sink; namespace ADSP::AudioRenderer { enum Message : u32 { - Invalid = 0x00, - MapUnmap_Map = 0x01, - MapUnmap_MapResponse = 0x02, - MapUnmap_Unmap = 0x03, - MapUnmap_UnmapResponse = 0x04, - MapUnmap_InvalidateCache = 0x05, - MapUnmap_InvalidateCacheResponse = 0x06, - MapUnmap_Shutdown = 0x07, - MapUnmap_ShutdownResponse = 0x08, - InitializeOK = 0x16, - RenderResponse = 0x20, - Render = 0x2A, - Shutdown = 0x34, + Invalid = 0, + MapUnmap_Map = 1, + MapUnmap_MapResponse = 2, + MapUnmap_Unmap = 3, + MapUnmap_UnmapResponse = 4, + MapUnmap_InvalidateCache = 5, + MapUnmap_InvalidateCacheResponse = 6, + MapUnmap_Shutdown = 7, + MapUnmap_ShutdownResponse = 8, + InitializeOK = 22, + RenderResponse = 32, + Render = 42, + Shutdown = 52, }; /** @@ -54,7 +47,7 @@ enum Message : u32 { */ class AudioRenderer { public: - explicit AudioRenderer(Core::System& system, Core::Memory::Memory& memory, Sink::Sink& sink); + explicit AudioRenderer(Core::System& system, Sink::Sink& sink); ~AudioRenderer(); /** @@ -72,8 +65,8 @@ public: void Signal(); void Wait(); - void Send(Direction dir, MailboxMessage message); - MailboxMessage Receive(Direction dir, bool block = true); + void Send(Direction dir, u32 message); + u32 Receive(Direction dir); void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, u64 applet_resource_user_id, bool reset) noexcept; @@ -94,9 +87,7 @@ private: /// Core system Core::System& system; - /// Memory - Core::Memory::Memory& memory; - /// The output sink the AudioRenderer will use + /// The output sink the AudioRenderer will send samples to Sink::Sink& sink; /// The active mailbox Mailbox mailbox; @@ -104,11 +95,13 @@ private: std::jthread main_thread{}; /// The current state std::atomic running{}; + /// Shared memory of input command buffers, set by host, read by DSP std::array command_buffers{}; /// The command lists to process std::array command_list_processors{}; /// The streams which will receive the processed samples std::array streams{}; + /// CPU Tick when the DSP was signalled to process, uses time rather than tick u64 signalled_tick{0}; }; diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp new file mode 100644 index 0000000000..2c16d37692 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_decode_object.h" +#include "common/assert.h" + +namespace AudioCore::ADSP::OpusDecoder { +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} +} // namespace + +u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { + if (!IsValidChannelCount(channel_count)) { + return 0; + } + return static_cast(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); +} + +OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { + auto* new_decoder = reinterpret_cast(buffer); + auto* comparison = reinterpret_cast(buffer2); + + if (new_decoder->magic == DecodeObjectMagic) { + if (!new_decoder->initialized || + (new_decoder->initialized && new_decoder->self == comparison)) { + new_decoder->state_valid = true; + } + } else { + new_decoder->initialized = false; + new_decoder->state_valid = true; + } + return *new_decoder; +} + +s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + return OPUS_OK; + } + + // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include + // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer + // provided. + // We could use _create and have libopus allocate it for us, but then we have to separately + // track which decoder is being used between this and multistream in order to call the correct + // destroy from the host side. + // This is a bit cringe, but is safe as these objects are only ever initialized inside the given + // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. + decoder = (LibOpusDecoder*)(this + 1); + s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); + if (ret == OPUS_OK) { + magic = DecodeObjectMagic; + initialized = true; + state_valid = true; + self = this; + final_range = 0; + } + return ret; +} + +s32 OpusDecodeObject::Shutdown() { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + magic = 0x0; + initialized = false; + state_valid = false; + self = nullptr; + final_range = 0; + decoder = nullptr; + } + return OPUS_OK; +} + +s32 OpusDecodeObject::ResetDecoder() { + return opus_decoder_ctl(decoder, OPUS_RESET_STATE); +} + +s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, + u64 input_data, u64 input_data_size) { + ASSERT(initialized); + out_sample_count = 0; + + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + auto ret_code_or_samples = opus_decode( + decoder, reinterpret_cast(input_data), static_cast(input_data_size), + reinterpret_cast(output_data), static_cast(output_data_size), 0); + + if (ret_code_or_samples < OPUS_OK) { + return ret_code_or_samples; + } + + out_sample_count = ret_code_or_samples; + return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); +} + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.h b/src/audio_core/adsp/apps/opus/opus_decode_object.h new file mode 100644 index 0000000000..6425f987c6 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace AudioCore::ADSP::OpusDecoder { +using LibOpusDecoder = ::OpusDecoder; +static constexpr u32 DecodeObjectMagic = 0xDEADBEEF; + +class OpusDecodeObject { +public: + static u32 GetWorkBufferSize(u32 channel_count); + static OpusDecodeObject& Initialize(u64 buffer, u64 buffer2); + + s32 InitializeDecoder(u32 sample_rate, u32 channel_count); + s32 Shutdown(); + s32 ResetDecoder(); + s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, + u64 input_data_size); + u32 GetFinalRange() const noexcept { + return final_range; + } + +private: + u32 magic; + bool initialized; + bool state_valid; + OpusDecodeObject* self; + u32 final_range; + LibOpusDecoder* decoder; +}; +static_assert(std::is_trivially_constructible_v); + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp new file mode 100644 index 0000000000..2084de1285 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp @@ -0,0 +1,269 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "audio_core/adsp/apps/opus/opus_decode_object.h" +#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" +#include "audio_core/adsp/apps/opus/shared_memory.h" +#include "audio_core/audio_core.h" +#include "audio_core/common/common.h" +#include "common/logging/log.h" +#include "common/microprofile.h" +#include "common/thread.h" +#include "core/core.h" +#include "core/core_timing.h" + +MICROPROFILE_DEFINE(OpusDecoder, "Audio", "DSP_OpusDecoder", MP_RGB(60, 19, 97)); + +namespace AudioCore::ADSP::OpusDecoder { + +namespace { +constexpr size_t OpusStreamCountMax = 255; + +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} + +bool IsValidMultiStreamChannelCount(u32 channel_count) { + return channel_count <= OpusStreamCountMax; +} + +bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) { + return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && + sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; +} +} // namespace + +OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} { + init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); }); +} + +OpusDecoder::~OpusDecoder() { + if (!running) { + init_thread.request_stop(); + return; + } + + // Shutdown the thread + Send(Direction::DSP, Message::Shutdown); + auto msg = Receive(Direction::Host); + ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", + Message::ShutdownOK, msg); + main_thread.request_stop(); + main_thread.join(); + running = false; +} + +void OpusDecoder::Send(Direction dir, u32 message) { + mailbox.Send(dir, std::move(message)); +} + +u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) { + return mailbox.Receive(dir, stop_token); +} + +void OpusDecoder::Init(std::stop_token stop_token) { + Common::SetCurrentThreadName("DSP_OpusDecoder_Init"); + + if (Receive(Direction::DSP, stop_token) != Message::Start) { + LOG_ERROR(Service_Audio, + "DSP OpusDecoder failed to receive Start message. Opus initialization failed."); + return; + } + main_thread = std::jthread([this](std::stop_token st) { Main(st); }); + running = true; + Send(Direction::Host, Message::StartOK); +} + +void OpusDecoder::Main(std::stop_token stop_token) { + Common::SetCurrentThreadName("DSP_OpusDecoder_Main"); + + while (!stop_token.stop_requested()) { + auto msg = Receive(Direction::DSP, stop_token); + switch (msg) { + case Shutdown: + Send(Direction::Host, Message::ShutdownOK); + return; + + case GetWorkBufferSize: { + auto channel_count = static_cast(shared_memory->host_send_data[0]); + + ASSERT(IsValidChannelCount(channel_count)); + + shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count); + Send(Direction::Host, Message::GetWorkBufferSizeOK); + } break; + + case InitializeDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + auto buffer_size = shared_memory->host_send_data[1]; + auto sample_rate = static_cast(shared_memory->host_send_data[2]); + auto channel_count = static_cast(shared_memory->host_send_data[3]); + + ASSERT(sample_rate >= 0); + ASSERT(IsValidChannelCount(channel_count)); + ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count)); + + auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = + decoder_object.InitializeDecoder(sample_rate, channel_count); + + Send(Direction::Host, Message::InitializeDecodeObjectOK); + } break; + + case ShutdownDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + + auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); + + Send(Direction::Host, Message::ShutdownDecodeObjectOK); + } break; + + case DecodeInterleaved: { + auto start_time = system.CoreTiming().GetGlobalTimeUs(); + + auto buffer = shared_memory->host_send_data[0]; + auto input_data = shared_memory->host_send_data[1]; + auto input_data_size = shared_memory->host_send_data[2]; + auto output_data = shared_memory->host_send_data[3]; + auto output_data_size = shared_memory->host_send_data[4]; + auto final_range = static_cast(shared_memory->host_send_data[5]); + auto reset_requested = shared_memory->host_send_data[6]; + + u32 decoded_samples{0}; + + auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); + s32 error_code{OPUS_OK}; + if (reset_requested) { + error_code = decoder_object.ResetDecoder(); + } + + if (error_code == OPUS_OK) { + error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, + input_data, input_data_size); + } + + if (error_code == OPUS_OK) { + if (final_range && decoder_object.GetFinalRange() != final_range) { + error_code = OPUS_INVALID_PACKET; + } + } + + auto end_time = system.CoreTiming().GetGlobalTimeUs(); + shared_memory->dsp_return_data[0] = error_code; + shared_memory->dsp_return_data[1] = decoded_samples; + shared_memory->dsp_return_data[2] = (end_time - start_time).count(); + + Send(Direction::Host, Message::DecodeInterleavedOK); + } break; + + case MapMemory: { + [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + Send(Direction::Host, Message::MapMemoryOK); + } break; + + case UnmapMemory: { + [[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + Send(Direction::Host, Message::UnmapMemoryOK); + } break; + + case GetWorkBufferSizeForMultiStream: { + auto total_stream_count = static_cast(shared_memory->host_send_data[0]); + auto stereo_stream_count = static_cast(shared_memory->host_send_data[1]); + + ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); + + shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize( + total_stream_count, stereo_stream_count); + Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK); + } break; + + case InitializeMultiStreamDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + auto buffer_size = shared_memory->host_send_data[1]; + auto sample_rate = static_cast(shared_memory->host_send_data[2]); + auto channel_count = static_cast(shared_memory->host_send_data[3]); + auto total_stream_count = static_cast(shared_memory->host_send_data[4]); + auto stereo_stream_count = static_cast(shared_memory->host_send_data[5]); + // Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel + // mappings, but [6] is never set, and there is not enough room in the argument data for + // more than 40 channels, when 255 are possible. + // It also means the mapping values are undefined, though likely always 0, + // and the mappings given by the game are ignored. The mappings are copied to this + // dedicated buffer host side, so let's do as intended. + auto mappings = shared_memory->channel_mapping.data(); + + ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count)); + ASSERT(sample_rate >= 0); + ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize( + total_stream_count, stereo_stream_count)); + + auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder( + sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings); + + Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK); + } break; + + case ShutdownMultiStreamDecodeObject: { + auto buffer = shared_memory->host_send_data[0]; + [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; + + auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); + shared_memory->dsp_return_data[0] = decoder_object.Shutdown(); + + Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK); + } break; + + case DecodeInterleavedForMultiStream: { + auto start_time = system.CoreTiming().GetGlobalTimeUs(); + + auto buffer = shared_memory->host_send_data[0]; + auto input_data = shared_memory->host_send_data[1]; + auto input_data_size = shared_memory->host_send_data[2]; + auto output_data = shared_memory->host_send_data[3]; + auto output_data_size = shared_memory->host_send_data[4]; + auto final_range = static_cast(shared_memory->host_send_data[5]); + auto reset_requested = shared_memory->host_send_data[6]; + + u32 decoded_samples{0}; + + auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer); + s32 error_code{OPUS_OK}; + if (reset_requested) { + error_code = decoder_object.ResetDecoder(); + } + + if (error_code == OPUS_OK) { + error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size, + input_data, input_data_size); + } + + if (error_code == OPUS_OK) { + if (final_range && decoder_object.GetFinalRange() != final_range) { + error_code = OPUS_INVALID_PACKET; + } + } + + auto end_time = system.CoreTiming().GetGlobalTimeUs(); + shared_memory->dsp_return_data[0] = error_code; + shared_memory->dsp_return_data[1] = decoded_samples; + shared_memory->dsp_return_data[2] = (end_time - start_time).count(); + + Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK); + } break; + + default: + LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg); + continue; + } + } +} + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.h b/src/audio_core/adsp/apps/opus/opus_decoder.h new file mode 100644 index 0000000000..fcb89bb40e --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_decoder.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "audio_core/adsp/apps/opus/shared_memory.h" +#include "audio_core/adsp/mailbox.h" +#include "common/common_types.h" + +namespace Core { +class System; +} // namespace Core + +namespace AudioCore::ADSP::OpusDecoder { + +enum Message : u32 { + Invalid = 0, + Start = 1, + Shutdown = 2, + StartOK = 11, + ShutdownOK = 12, + GetWorkBufferSize = 21, + InitializeDecodeObject = 22, + ShutdownDecodeObject = 23, + DecodeInterleaved = 24, + MapMemory = 25, + UnmapMemory = 26, + GetWorkBufferSizeForMultiStream = 27, + InitializeMultiStreamDecodeObject = 28, + ShutdownMultiStreamDecodeObject = 29, + DecodeInterleavedForMultiStream = 30, + + GetWorkBufferSizeOK = 41, + InitializeDecodeObjectOK = 42, + ShutdownDecodeObjectOK = 43, + DecodeInterleavedOK = 44, + MapMemoryOK = 45, + UnmapMemoryOK = 46, + GetWorkBufferSizeForMultiStreamOK = 47, + InitializeMultiStreamDecodeObjectOK = 48, + ShutdownMultiStreamDecodeObjectOK = 49, + DecodeInterleavedForMultiStreamOK = 50, +}; + +/** + * The AudioRenderer application running on the ADSP. + */ +class OpusDecoder { +public: + explicit OpusDecoder(Core::System& system); + ~OpusDecoder(); + + bool IsRunning() const noexcept { + return running; + } + + void Send(Direction dir, u32 message); + u32 Receive(Direction dir, std::stop_token stop_token = {}); + + void SetSharedMemory(SharedMemory& shared_memory_) { + shared_memory = &shared_memory_; + } + +private: + /** + * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread. + */ + void Init(std::stop_token stop_token); + /** + * Main OpusDecoder thread, responsible for processing the incoming Opus packets. + */ + void Main(std::stop_token stop_token); + + /// Core system + Core::System& system; + /// Mailbox to communicate messages with the host, drives the main thread + Mailbox mailbox; + /// Init thread + std::jthread init_thread{}; + /// Main thread + std::jthread main_thread{}; + /// The current state + bool running{}; + /// Structure shared with the host, input data set by the host before sending a mailbox message, + /// and the responses are written back by the OpusDecoder. + SharedMemory* shared_memory{}; +}; + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp new file mode 100644 index 0000000000..f6d362e68c --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" +#include "common/assert.h" + +namespace AudioCore::ADSP::OpusDecoder { + +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} + +bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { + return total_stream_count > 0 && stereo_stream_count > 0 && + stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); +} +} // namespace + +u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, + u32 stereo_stream_count) { + if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { + return static_cast(sizeof(OpusMultiStreamDecodeObject)) + + opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); + } + return 0; +} + +OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { + auto* new_decoder = reinterpret_cast(buffer); + auto* comparison = reinterpret_cast(buffer2); + + if (new_decoder->magic == DecodeMultiStreamObjectMagic) { + if (!new_decoder->initialized || + (new_decoder->initialized && new_decoder->self == comparison)) { + new_decoder->state_valid = true; + } + } else { + new_decoder->initialized = false; + new_decoder->state_valid = true; + } + return *new_decoder; +} + +s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, + u32 channel_count, u32 stereo_stream_count, + u8* mappings) { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + return OPUS_OK; + } + + // See OpusDecodeObject::InitializeDecoder for an explanation of this + decoder = (LibOpusMSDecoder*)(this + 1); + s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, + stereo_stream_count, mappings); + if (ret == OPUS_OK) { + magic = DecodeMultiStreamObjectMagic; + initialized = true; + state_valid = true; + self = this; + final_range = 0; + } + return ret; +} + +s32 OpusMultiStreamDecodeObject::Shutdown() { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + magic = 0x0; + initialized = false; + state_valid = false; + self = nullptr; + final_range = 0; + decoder = nullptr; + } + return OPUS_OK; +} + +s32 OpusMultiStreamDecodeObject::ResetDecoder() { + return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); +} + +s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, + u64 output_data_size, u64 input_data, u64 input_data_size) { + ASSERT(initialized); + out_sample_count = 0; + + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + auto ret_code_or_samples = opus_multistream_decode( + decoder, reinterpret_cast(input_data), static_cast(input_data_size), + reinterpret_cast(output_data), static_cast(output_data_size), 0); + + if (ret_code_or_samples < OPUS_OK) { + return ret_code_or_samples; + } + + out_sample_count = ret_code_or_samples; + return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); +} + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h new file mode 100644 index 0000000000..93558ded5d --- /dev/null +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace AudioCore::ADSP::OpusDecoder { +using LibOpusMSDecoder = ::OpusMSDecoder; +static constexpr u32 DecodeMultiStreamObjectMagic = 0xDEADBEEF; + +class OpusMultiStreamDecodeObject { +public: + static u32 GetWorkBufferSize(u32 total_stream_count, u32 stereo_stream_count); + static OpusMultiStreamDecodeObject& Initialize(u64 buffer, u64 buffer2); + + s32 InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count, + u32 stereo_stream_count, u8* mappings); + s32 Shutdown(); + s32 ResetDecoder(); + s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, + u64 input_data_size); + u32 GetFinalRange() const noexcept { + return final_range; + } + +private: + u32 magic; + bool initialized; + bool state_valid; + OpusMultiStreamDecodeObject* self; + u32 final_range; + LibOpusMSDecoder* decoder; +}; +static_assert(std::is_trivially_constructible_v); + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/shared_memory.h b/src/audio_core/adsp/apps/opus/shared_memory.h new file mode 100644 index 0000000000..c696731ed8 --- /dev/null +++ b/src/audio_core/adsp/apps/opus/shared_memory.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace AudioCore::ADSP::OpusDecoder { + +struct SharedMemory { + std::array channel_mapping{}; + std::array host_send_data{}; + std::array dsp_return_data{}; +}; + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/mailbox.h b/src/audio_core/adsp/mailbox.h index c31b737177..1dd40ebfa7 100644 --- a/src/audio_core/adsp/mailbox.h +++ b/src/audio_core/adsp/mailbox.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "common/bounded_threadsafe_queue.h" #include "common/common_types.h" @@ -19,11 +21,6 @@ enum class Direction : u32 { DSP, }; -struct MailboxMessage { - u32 msg; - std::span data; -}; - class Mailbox { public: void Initialize(AppMailboxId id_) { @@ -35,25 +32,19 @@ public: return id; } - void Send(Direction dir, MailboxMessage&& message) { + void Send(Direction dir, u32 message) { auto& queue = dir == Direction::Host ? host_queue : adsp_queue; - queue.EmplaceWait(std::move(message)); + queue.EmplaceWait(message); } - MailboxMessage Receive(Direction dir, bool block = true) { + u32 Receive(Direction dir, std::stop_token stop_token = {}) { auto& queue = dir == Direction::Host ? host_queue : adsp_queue; - MailboxMessage t; - if (block) { - queue.PopWait(t); - } else { - queue.TryPop(t); - } - return t; + return queue.PopWait(stop_token); } void Reset() { id = AppMailboxId::Invalid; - MailboxMessage t; + u32 t{}; while (host_queue.TryPop(t)) { } while (adsp_queue.TryPop(t)) { @@ -62,8 +53,8 @@ public: private: AppMailboxId id{0}; - Common::SPSCQueue host_queue; - Common::SPSCQueue adsp_queue; + Common::SPSCQueue host_queue; + Common::SPSCQueue adsp_queue; }; } // namespace AudioCore::ADSP diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp new file mode 100644 index 0000000000..5b23fce148 --- /dev/null +++ b/src/audio_core/opus/decoder.cpp @@ -0,0 +1,179 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/opus/decoder.h" +#include "audio_core/opus/hardware_opus.h" +#include "audio_core/opus/parameters.h" +#include "common/alignment.h" +#include "common/swap.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +using namespace Service::Audio; +namespace { +OpusPacketHeader ReverseHeader(OpusPacketHeader header) { + OpusPacketHeader out; + out.size = Common::swap32(header.size); + out.final_range = Common::swap32(header.final_range); + return out; +} +} // namespace + +OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) + : system{system_}, hardware_opus{hardware_opus_} {} + +OpusDecoder::~OpusDecoder() { + if (decode_object_initialized) { + hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); + } +} + +Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size) { + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + shared_buffer_size = transfer_memory_size; + shared_buffer = std::make_unique(shared_buffer_size); + shared_memory_mapped = true; + + buffer_size = + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); + + out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; + size_t in_data_size{0x600u}; + in_data = {out_data.data() - in_data_size, in_data_size}; + + ON_RESULT_FAILURE { + if (shared_memory_mapped) { + shared_memory_mapped = false; + ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); + } + }; + + R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, + shared_buffer.get(), shared_buffer_size)); + + sample_rate = params.sample_rate; + channel_count = params.channel_count; + use_large_frame_size = params.use_large_frame_size; + decode_object_initialized = true; + R_SUCCEED(); +} + +Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + shared_buffer_size = transfer_memory_size; + shared_buffer = std::make_unique(shared_buffer_size); + shared_memory_mapped = true; + + buffer_size = + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); + + out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; + size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; + in_data = {out_data.data() - in_data_size, in_data_size}; + + ON_RESULT_FAILURE { + if (shared_memory_mapped) { + shared_memory_mapped = false; + ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); + } + }; + + R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( + params.sample_rate, params.channel_count, params.total_stream_count, + params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), + shared_buffer_size)); + + sample_rate = params.sample_rate; + channel_count = params.channel_count; + total_stream_count = params.total_stream_count; + stereo_stream_count = params.stereo_stream_count; + use_large_frame_size = params.use_large_frame_size; + decode_object_initialized = true; + R_SUCCEED(); +} + +Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, std::span input_data, + std::span output_data, bool reset) { + u32 out_samples; + u64 time_taken{}; + + R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); + + auto* header_p{reinterpret_cast(input_data.data())}; + OpusPacketHeader header{ReverseHeader(*header_p)}; + + R_UNLESS(in_data.size_bytes() >= header.size && + header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), + ResultBufferTooSmall); + + if (!shared_memory_mapped) { + R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); + shared_memory_mapped = true; + } + + std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); + + R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), + channel_count, in_data.data(), header.size, + shared_buffer.get(), time_taken, reset)); + + std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); + + *out_data_size = header.size + sizeof(OpusPacketHeader); + *out_sample_count = out_samples; + if (out_time_taken) { + *out_time_taken = time_taken / 1000; + } + R_SUCCEED(); +} + +Result OpusDecoder::SetContext([[maybe_unused]] std::span context) { + R_SUCCEED_IF(shared_memory_mapped); + shared_memory_mapped = true; + R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); +} + +Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, + std::span input_data, + std::span output_data, bool reset) { + u32 out_samples; + u64 time_taken{}; + + R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); + + auto* header_p{reinterpret_cast(input_data.data())}; + OpusPacketHeader header{ReverseHeader(*header_p)}; + + LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", + header.size, input_data.size_bytes(), in_data.size_bytes()); + + R_UNLESS(in_data.size_bytes() >= header.size && + header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), + ResultBufferTooSmall); + + if (!shared_memory_mapped) { + R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); + shared_memory_mapped = true; + } + + std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); + + R_TRY(hardware_opus.DecodeInterleavedForMultiStream( + out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), + header.size, shared_buffer.get(), time_taken, reset)); + + std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); + + *out_data_size = header.size + sizeof(OpusPacketHeader); + *out_sample_count = out_samples; + if (out_time_taken) { + *out_time_taken = time_taken / 1000; + } + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h new file mode 100644 index 0000000000..d08d8a4a4d --- /dev/null +++ b/src/audio_core/opus/decoder.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "audio_core/opus/parameters.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/audio/errors.h" + +namespace Core { +class System; +} + +namespace AudioCore::OpusDecoder { +class HardwareOpus; + +class OpusDecoder { +public: + explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); + ~OpusDecoder(); + + Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size); + Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size); + Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, + std::span input_data, std::span output_data, bool reset); + Result SetContext([[maybe_unused]] std::span context); + Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, std::span input_data, + std::span output_data, bool reset); + +private: + Core::System& system; + HardwareOpus& hardware_opus; + std::unique_ptr shared_buffer{}; + u64 shared_buffer_size; + std::span in_data{}; + std::span out_data{}; + u64 buffer_size{}; + s32 sample_rate{}; + s32 channel_count{}; + bool use_large_frame_size{false}; + s32 total_stream_count{}; + s32 stereo_stream_count{}; + bool shared_memory_mapped{false}; + bool decode_object_initialized{false}; +}; + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp new file mode 100644 index 0000000000..4a5382973a --- /dev/null +++ b/src/audio_core/opus/decoder_manager.cpp @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_decoder.h" +#include "audio_core/opus/decoder_manager.h" +#include "common/alignment.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +using namespace Service::Audio; + +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} + +bool IsValidMultiStreamChannelCount(u32 channel_count) { + return channel_count > 0 && channel_count <= OpusStreamCountMax; +} + +bool IsValidSampleRate(u32 sample_rate) { + return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || + sample_rate == 24'000 || sample_rate == 48'000; +} + +bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { + return total_stream_count > 0 && stereo_stream_count > 0 && + stereo_stream_count <= total_stream_count && + total_stream_count + stereo_stream_count <= channel_count; +} + +} // namespace + +OpusDecoderManager::OpusDecoderManager(Core::System& system_) + : system{system_}, hardware_opus{system} { + for (u32 i = 0; i < MaxChannels; i++) { + required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); + } +} + +Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { + OpusParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .use_large_frame_size = false, + }; + R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { + R_RETURN(GetWorkBufferSizeExEx(params, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { + R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); + R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); + + auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + work_buffer_size += + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); + out_size = work_buffer_size + 0x600; + R_SUCCEED(); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, + u64& out_size) { + OpusMultiStreamParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .total_stream_count = params.total_stream_count, + .stereo_stream_count = params.stereo_stream_count, + .use_large_frame_size = false, + .mappings = {}, + }; + R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, + u64& out_size) { + R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, + u64& out_size) { + R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); + R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); + R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, + params.stereo_stream_count), + ResultInvalidOpusSampleRate); + + auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( + params.total_stream_count, params.stereo_stream_count)}; + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); + work_buffer_size += + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); + out_size = work_buffer_size; + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h new file mode 100644 index 0000000000..466e1967b8 --- /dev/null +++ b/src/audio_core/opus/decoder_manager.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/opus/hardware_opus.h" +#include "audio_core/opus/parameters.h" +#include "common/common_types.h" +#include "core/hle/service/audio/errors.h" + +namespace Core { +class System; +} + +namespace AudioCore::OpusDecoder { + +class OpusDecoderManager { +public: + OpusDecoderManager(Core::System& system); + + HardwareOpus& GetHardwareOpus() { + return hardware_opus; + } + + Result GetWorkBufferSize(OpusParameters& params, u64& out_size); + Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); + Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); + Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); + Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); + Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); + +private: + Core::System& system; + HardwareOpus hardware_opus; + std::array required_workbuffer_sizes{}; +}; + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp new file mode 100644 index 0000000000..d6544dcb05 --- /dev/null +++ b/src/audio_core/opus/hardware_opus.cpp @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "audio_core/audio_core.h" +#include "audio_core/opus/hardware_opus.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +namespace { +using namespace Service::Audio; + +static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { + s32 error{static_cast(error_code)}; + ASSERT(error <= OPUS_OK); + switch (error) { + case OPUS_ALLOC_FAIL: + R_THROW(ResultLibOpusAllocFail); + case OPUS_INVALID_STATE: + R_THROW(ResultLibOpusInvalidState); + case OPUS_UNIMPLEMENTED: + R_THROW(ResultLibOpusUnimplemented); + case OPUS_INVALID_PACKET: + R_THROW(ResultLibOpusInvalidPacket); + case OPUS_INTERNAL_ERROR: + R_THROW(ResultLibOpusInternalError); + case OPUS_BUFFER_TOO_SMALL: + R_THROW(ResultBufferTooSmall); + case OPUS_BAD_ARG: + R_THROW(ResultLibOpusBadArg); + case OPUS_OK: + R_RETURN(ResultSuccess); + } + UNREACHABLE(); +} + +} // namespace + +HardwareOpus::HardwareOpus(Core::System& system_) + : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { + opus_decoder.SetSharedMemory(shared_memory); +} + +u64 HardwareOpus::GetWorkBufferSize(u32 channel) { + if (!opus_decoder.IsRunning()) { + return 0; + } + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = channel; + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); + return 0; + } + return shared_memory.dsp_return_data[0]; +} + +u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = total_stream_count; + shared_memory.host_send_data[1] = stereo_stream_count; + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); + return 0; + } + return shared_memory.dsp_return_data[0]; +} + +Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, + u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + shared_memory.host_send_data[2] = sample_rate; + shared_memory.host_send_data[3] = channel_count; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, + u32 total_stream_count, + u32 stereo_stream_count, void* mappings, + void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + shared_memory.host_send_data[2] = sample_rate; + shared_memory.host_send_data[3] = channel_count; + shared_memory.host_send_data[4] = total_stream_count; + shared_memory.host_send_data[5] = stereo_stream_count; + + ASSERT(channel_count <= MaxChannels); + std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, + "Expected Opus shutdown code {}, got {}", + ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, + "Expected Opus shutdown code {}, got {}", + ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, void* input_data, + u64 input_data_size, void* buffer, u64& out_time_taken, + bool reset) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = (u64)input_data; + shared_memory.host_send_data[2] = input_data_size; + shared_memory.host_send_data[3] = (u64)output_data; + shared_memory.host_send_data[4] = output_data_size; + shared_memory.host_send_data[5] = 0; + shared_memory.host_send_data[6] = reset; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + auto error_code{static_cast(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { + out_sample_count = static_cast(shared_memory.dsp_return_data[1]); + out_time_taken = 1000 * shared_memory.dsp_return_data[2]; + } + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); +} + +Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, + void* input_data, u64 input_data_size, + void* buffer, u64& out_time_taken, + bool reset) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = (u64)input_data; + shared_memory.host_send_data[2] = input_data_size; + shared_memory.host_send_data[3] = (u64)output_data; + shared_memory.host_send_data[4] = output_data_size; + shared_memory.host_send_data[5] = 0; + shared_memory.host_send_data[6] = reset; + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + auto error_code{static_cast(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { + out_sample_count = static_cast(shared_memory.dsp_return_data[1]); + out_time_taken = 1000 * shared_memory.dsp_return_data[2]; + } + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); +} + +Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::MapMemoryOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + R_SUCCEED(); +} + +Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h new file mode 100644 index 0000000000..7013a6b404 --- /dev/null +++ b/src/audio_core/opus/hardware_opus.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "audio_core/adsp/apps/opus/opus_decoder.h" +#include "audio_core/adsp/apps/opus/shared_memory.h" +#include "audio_core/adsp/mailbox.h" +#include "core/hle/service/audio/errors.h" + +namespace AudioCore::OpusDecoder { +class HardwareOpus { +public: + HardwareOpus(Core::System& system); + + u64 GetWorkBufferSize(u32 channel); + u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); + + Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, + u64 buffer_size); + Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, + u32 totaL_stream_count, u32 stereo_stream_count, + void* mappings, void* buffer, u64 buffer_size); + Result ShutdownDecodeObject(void* buffer, u64 buffer_size); + Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); + Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, + u32 channel_count, void* input_data, u64 input_data_size, void* buffer, + u64& out_time_taken, bool reset); + Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, + void* input_data, u64 input_data_size, void* buffer, + u64& out_time_taken, bool reset); + Result MapMemory(void* buffer, u64 buffer_size); + Result UnmapMemory(void* buffer, u64 buffer_size); + +private: + Core::System& system; + std::mutex mutex; + ADSP::OpusDecoder::OpusDecoder& opus_decoder; + ADSP::OpusDecoder::SharedMemory shared_memory; +}; +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/parameters.h b/src/audio_core/opus/parameters.h new file mode 100644 index 0000000000..4c54b28250 --- /dev/null +++ b/src/audio_core/opus/parameters.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace AudioCore::OpusDecoder { +constexpr size_t OpusStreamCountMax = 255; +constexpr size_t MaxChannels = 2; + +struct OpusParameters { + /* 0x00 */ u32 sample_rate; + /* 0x04 */ u32 channel_count; +}; // size = 0x8 +static_assert(sizeof(OpusParameters) == 0x8, "OpusParameters has the wrong size!"); + +struct OpusParametersEx { + /* 0x00 */ u32 sample_rate; + /* 0x04 */ u32 channel_count; + /* 0x08 */ bool use_large_frame_size; + /* 0x09 */ INSERT_PADDING_BYTES(7); +}; // size = 0x10 +static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!"); + +struct OpusMultiStreamParameters { + /* 0x00 */ u32 sample_rate; + /* 0x04 */ u32 channel_count; + /* 0x08 */ u32 total_stream_count; + /* 0x0C */ u32 stereo_stream_count; + /* 0x10 */ std::array mappings; +}; // size = 0x110 +static_assert(sizeof(OpusMultiStreamParameters) == 0x110, + "OpusMultiStreamParameters has the wrong size!"); + +struct OpusMultiStreamParametersEx { + /* 0x00 */ u32 sample_rate; + /* 0x04 */ u32 channel_count; + /* 0x08 */ u32 total_stream_count; + /* 0x0C */ u32 stereo_stream_count; + /* 0x10 */ bool use_large_frame_size; + /* 0x11 */ INSERT_PADDING_BYTES(7); + /* 0x18 */ std::array mappings; +}; // size = 0x118 +static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, + "OpusMultiStreamParametersEx has the wrong size!"); + +struct OpusPacketHeader { + /* 0x00 */ u32 size; + /* 0x04 */ u32 final_range; +}; // size = 0x8 +static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusPacketHeader has the wrong size!"); +} // namespace AudioCore::OpusDecoder diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 34877b461a..416203c598 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -26,12 +26,11 @@ add_library(common STATIC assert.h atomic_helpers.h atomic_ops.h - detached_tasks.cpp - detached_tasks.h bit_cast.h bit_field.h bit_set.h bit_util.h + bounded_threadsafe_queue.h cityhash.cpp cityhash.h common_funcs.h @@ -41,6 +40,8 @@ add_library(common STATIC container_hash.h demangle.cpp demangle.h + detached_tasks.cpp + detached_tasks.h div_ceil.h dynamic_library.cpp dynamic_library.h diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index bd87aa09b5..b36fc1de96 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h @@ -45,13 +45,13 @@ public: } T PopWait() { - T t; + T t{}; Pop(t); return t; } T PopWait(std::stop_token stop_token) { - T t; + T t{}; Pop(t, stop_token); return t; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 30d2f7df64..b2dc71d4cb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -890,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 renderdoc) +target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls renderdoc) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) endif() diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 92a1439eb4..dd0b27f479 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -62,7 +62,7 @@ enum class ErrorModule : u32 { XCD = 108, TMP451 = 109, NIFM = 110, - Hwopus = 111, + HwOpus = 111, LSM6DS3 = 112, Bluetooth = 113, VI = 114, diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index 3d3d3d97a3..c41345f7e7 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h @@ -20,4 +20,16 @@ constexpr Result ResultNotSupported{ErrorModule::Audio, 513}; constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; +constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7}; +constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8}; +constexpr Result ResultLibOpusInvalidState{ErrorModule::HwOpus, 6}; +constexpr Result ResultLibOpusUnimplemented{ErrorModule::HwOpus, 5}; +constexpr Result ResultLibOpusInvalidPacket{ErrorModule::HwOpus, 17}; +constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4}; +constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3}; +constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2}; +constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259}; +constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001}; +constexpr Result ResultInvalidOpusChannelCount{ErrorModule::HwOpus, 1002}; + } // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 1557e60885..6a7bf94162 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -1,420 +1,506 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include #include #include -#include -#include - +#include "audio_core/opus/decoder.h" +#include "audio_core/opus/parameters.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/scratch_buffer.h" +#include "core/core.h" #include "core/hle/service/audio/hwopus.h" #include "core/hle/service/ipc_helpers.h" namespace Service::Audio { -namespace { -struct OpusDeleter { - void operator()(OpusMSDecoder* ptr) const { - opus_multistream_decoder_destroy(ptr); - } -}; +using namespace AudioCore::OpusDecoder; -using OpusDecoderPtr = std::unique_ptr; - -struct OpusPacketHeader { - // Packet size in bytes. - u32_be size; - // Indicates the final range of the codec's entropy coder. - u32_be final_range; -}; -static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); - -class OpusDecoderState { +class IHardwareOpusDecoder final : public ServiceFramework { public: - /// Describes extra behavior that may be asked of the decoding context. - enum class ExtraBehavior { - /// No extra behavior. - None, - - /// Resets the decoder context back to a freshly initialized state. - ResetContext, - }; - - enum class PerfTime { - Disabled, - Enabled, - }; - - explicit OpusDecoderState(OpusDecoderPtr decoder_, u32 sample_rate_, u32 channel_count_) - : decoder{std::move(decoder_)}, sample_rate{sample_rate_}, channel_count{channel_count_} {} - - // Decodes interleaved Opus packets. Optionally allows reporting time taken to - // perform the decoding, as well as any relevant extra behavior. - void DecodeInterleaved(HLERequestContext& ctx, PerfTime perf_time, - ExtraBehavior extra_behavior) { - if (perf_time == PerfTime::Disabled) { - DecodeInterleavedHelper(ctx, nullptr, extra_behavior); - } else { - u64 performance = 0; - DecodeInterleavedHelper(ctx, &performance, extra_behavior); - } - } - -private: - void DecodeInterleavedHelper(HLERequestContext& ctx, u64* performance, - ExtraBehavior extra_behavior) { - u32 consumed = 0; - u32 sample_count = 0; - samples.resize_destructive(ctx.GetWriteBufferNumElements()); - - if (extra_behavior == ExtraBehavior::ResetContext) { - ResetDecoderContext(); - } - - if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { - LOG_ERROR(Audio, "Failed to decode opus data"); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(ogniK): Use correct error code - rb.Push(ResultUnknown); - return; - } - - const u32 param_size = performance != nullptr ? 6 : 4; - IPC::ResponseBuilder rb{ctx, param_size}; - rb.Push(ResultSuccess); - rb.Push(consumed); - rb.Push(sample_count); - if (performance) { - rb.Push(*performance); - } - ctx.WriteBuffer(samples); - } - - bool DecodeOpusData(u32& consumed, u32& sample_count, std::span input, - std::span output, u64* out_performance_time) const { - const auto start_time = std::chrono::steady_clock::now(); - const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); - if (sizeof(OpusPacketHeader) > input.size()) { - LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", - sizeof(OpusPacketHeader), input.size()); - return false; - } - - OpusPacketHeader hdr{}; - std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader)); - if (sizeof(OpusPacketHeader) + static_cast(hdr.size) > input.size()) { - LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", - sizeof(OpusPacketHeader) + static_cast(hdr.size), input.size()); - return false; - } - - const auto frame = input.data() + sizeof(OpusPacketHeader); - const auto decoded_sample_count = opus_packet_get_nb_samples( - frame, static_cast(input.size() - sizeof(OpusPacketHeader)), - static_cast(sample_rate)); - if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { - LOG_ERROR( - Audio, - "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}", - decoded_sample_count * channel_count * sizeof(u16), raw_output_sz); - return false; - } - - const int frame_size = (static_cast(raw_output_sz / sizeof(s16) / channel_count)); - const auto out_sample_count = - opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); - if (out_sample_count < 0) { - LOG_ERROR(Audio, - "Incorrect sample count received from opus_decode, " - "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", - out_sample_count, frame_size, static_cast(hdr.size)); - return false; - } - - const auto end_time = std::chrono::steady_clock::now() - start_time; - sample_count = out_sample_count; - consumed = static_cast(sizeof(OpusPacketHeader) + hdr.size); - if (out_performance_time != nullptr) { - *out_performance_time = - std::chrono::duration_cast(end_time).count(); - } - - return true; - } - - void ResetDecoderContext() { - ASSERT(decoder != nullptr); - - opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE); - } - - OpusDecoderPtr decoder; - u32 sample_rate; - u32 channel_count; - Common::ScratchBuffer samples; -}; - -class IHardwareOpusDecoderManager final : public ServiceFramework { -public: - explicit IHardwareOpusDecoderManager(Core::System& system_, OpusDecoderState decoder_state_) - : ServiceFramework{system_, "IHardwareOpusDecoderManager"}, decoder_state{ - std::move(decoder_state_)} { + explicit IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus) + : ServiceFramework{system_, "IHardwareOpusDecoder"}, + impl{std::make_unique(system_, hardware_opus)} { // clang-format off static const FunctionInfo functions[] = { - {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"}, - {1, nullptr, "SetContext"}, - {2, nullptr, "DecodeInterleavedForMultiStreamOld"}, - {3, nullptr, "SetContextForMultiStream"}, - {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, - {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, - {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, - {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, - {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, - {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, + {0, &IHardwareOpusDecoder::DecodeInterleavedOld, "DecodeInterleavedOld"}, + {1, &IHardwareOpusDecoder::SetContext, "SetContext"}, + {2, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld, "DecodeInterleavedForMultiStreamOld"}, + {3, &IHardwareOpusDecoder::SetContextForMultiStream, "SetContextForMultiStream"}, + {4, &IHardwareOpusDecoder::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, + {5, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld, "DecodeInterleavedForMultiStreamWithPerfOld"}, + {6, &IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld, "DecodeInterleavedWithPerfAndResetOld"}, + {7, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, + {8, &IHardwareOpusDecoder::DecodeInterleaved, "DecodeInterleaved"}, + {9, &IHardwareOpusDecoder::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, }; // clang-format on RegisterHandlers(functions); } + Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size) { + return impl->Initialize(params, transfer_memory, transfer_memory_size); + } + + Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size) { + return impl->Initialize(params, transfer_memory, transfer_memory_size); + } + private: void DecodeInterleavedOld(HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called"); + IPC::RequestParser rp{ctx}; - decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled, - OpusDecoderState::ExtraBehavior::None); + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + auto result = + impl->DecodeInterleaved(&size, nullptr, &sample_count, input_data, output_data, false); + + LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + } + + void SetContext(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_DEBUG(Service_Audio, "called"); + + auto input_data{ctx.ReadBuffer(0)}; + auto result = impl->SetContext(input_data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void DecodeInterleavedForMultiStreamOld(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + auto result = impl->DecodeInterleavedForMultiStream(&size, nullptr, &sample_count, + input_data, output_data, false); + + LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + } + + void SetContextForMultiStream(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_DEBUG(Service_Audio, "called"); + + auto input_data{ctx.ReadBuffer(0)}; + auto result = impl->SetContext(input_data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called"); + IPC::RequestParser rp{ctx}; - decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, - OpusDecoderState::ExtraBehavior::None); + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + u64 time_taken{}; + auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, + output_data, false); + + LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, + sample_count, time_taken); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + rb.Push(time_taken); + } + + void DecodeInterleavedForMultiStreamWithPerfOld(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + u64 time_taken{}; + auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, + input_data, output_data, false); + + LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, + sample_count, time_taken); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + rb.Push(time_taken); + } + + void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto reset{rp.Pop()}; + + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + u64 time_taken{}; + auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, + output_data, reset); + + LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", + reset, size, sample_count, time_taken); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + rb.Push(time_taken); + } + + void DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto reset{rp.Pop()}; + + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + u64 time_taken{}; + auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, + input_data, output_data, reset); + + LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", + reset, size, sample_count, time_taken); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + rb.Push(time_taken); } void DecodeInterleaved(HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called"); - IPC::RequestParser rp{ctx}; - const auto extra_behavior = rp.Pop() ? OpusDecoderState::ExtraBehavior::ResetContext - : OpusDecoderState::ExtraBehavior::None; - decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); + auto reset{rp.Pop()}; + + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + u64 time_taken{}; + auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, + output_data, reset); + + LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", + reset, size, sample_count, time_taken); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + rb.Push(time_taken); } void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called"); - IPC::RequestParser rp{ctx}; - const auto extra_behavior = rp.Pop() ? OpusDecoderState::ExtraBehavior::ResetContext - : OpusDecoderState::ExtraBehavior::None; - decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); + auto reset{rp.Pop()}; + + auto input_data{ctx.ReadBuffer(0)}; + output_data.resize_destructive(ctx.GetWriteBufferSize()); + + u32 size{}; + u32 sample_count{}; + u64 time_taken{}; + auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, + input_data, output_data, reset); + + LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", + reset, size, sample_count, time_taken); + + ctx.WriteBuffer(output_data); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.Push(size); + rb.Push(sample_count); + rb.Push(time_taken); } - OpusDecoderState decoder_state; + std::unique_ptr impl; + Common::ScratchBuffer output_data; }; -std::size_t WorkerBufferSize(u32 channel_count) { - ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); - constexpr int num_streams = 1; - const int num_stereo_streams = channel_count == 2 ? 1 : 0; - return opus_multistream_decoder_get_size(num_streams, num_stereo_streams); -} - -// Creates the mapping table that maps the input channels to the particular -// output channels. In the stereo case, we map the left and right input channels -// to the left and right output channels respectively. -// -// However, in the monophonic case, we only map the one available channel -// to the sole output channel. We specify 255 for the would-be right channel -// as this is a special value defined by Opus to indicate to the decoder to -// ignore that channel. -std::array CreateMappingTable(u32 channel_count) { - if (channel_count == 2) { - return {{0, 1}}; - } - - return {{0, 255}}; -} -} // Anonymous namespace - -void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto sample_rate = rp.Pop(); - const auto channel_count = rp.Pop(); - - LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); - - ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || - sample_rate == 12000 || sample_rate == 8000, - "Invalid sample rate"); - ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); - - const u32 worker_buffer_sz = static_cast(WorkerBufferSize(channel_count)); - LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(worker_buffer_sz); -} - -void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { - GetWorkBufferSize(ctx); -} - -void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { - GetWorkBufferSizeEx(ctx); -} - -void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { - OpusMultiStreamParametersEx param; - std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); - - const auto sample_rate = param.sample_rate; - const auto channel_count = param.channel_count; - const auto number_streams = param.number_streams; - const auto number_stereo_streams = param.number_stereo_streams; - - LOG_DEBUG( - Audio, - "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", - sample_rate, channel_count, number_streams, number_stereo_streams); - - ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || - sample_rate == 12000 || sample_rate == 8000, - "Invalid sample rate"); - - const u32 worker_buffer_sz = - static_cast(opus_multistream_decoder_get_size(number_streams, number_stereo_streams)); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(worker_buffer_sz); -} - void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto sample_rate = rp.Pop(); - const auto channel_count = rp.Pop(); - const auto buffer_sz = rp.Pop(); - LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, - channel_count, buffer_sz); + auto params = rp.PopRaw(); + auto transfer_memory_size{rp.Pop()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ + system.ApplicationProcess()->GetHandleTable().GetObject( + transfer_memory_handle)}; - ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || - sample_rate == 12000 || sample_rate == 8000, - "Invalid sample rate"); - ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", + params.sample_rate, params.channel_count, transfer_memory_size); - const std::size_t worker_sz = WorkerBufferSize(channel_count); - ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); + auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; - const int num_stereo_streams = channel_count == 2 ? 1 : 0; - const auto mapping_table = CreateMappingTable(channel_count); - - int error = 0; - OpusDecoderPtr decoder{ - opus_multistream_decoder_create(sample_rate, static_cast(channel_count), 1, - num_stereo_streams, mapping_table.data(), &error)}; - if (error != OPUS_OK || decoder == nullptr) { - LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(ogniK): Use correct error code - rb.Push(ResultUnknown); - return; - } + OpusParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .use_large_frame_size = false, + }; + auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface( - system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); + rb.Push(result); + rb.PushIpcInterface(decoder); +} + +void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto params = rp.PopRaw(); + + u64 size{}; + auto result = impl.GetWorkBufferSize(params, size); + + LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}", + params.sample_rate, params.channel_count, size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); +} + +void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input{ctx.ReadBuffer()}; + OpusMultiStreamParameters params; + std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); + + auto transfer_memory_size{rp.Pop()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ + system.ApplicationProcess()->GetHandleTable().GetObject( + transfer_memory_handle)}; + + LOG_DEBUG(Service_Audio, + "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " + "transfer_memory_size 0x{:X}", + params.sample_rate, params.channel_count, params.total_stream_count, + params.stereo_stream_count, transfer_memory_size); + + auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; + + OpusMultiStreamParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .total_stream_count = params.total_stream_count, + .stereo_stream_count = params.stereo_stream_count, + .use_large_frame_size = false, + .mappings{}, + }; + std::memcpy(ex.mappings.data(), params.mappings.data(), sizeof(params.mappings)); + auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(result); + rb.PushIpcInterface(decoder); +} + +void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input{ctx.ReadBuffer()}; + OpusMultiStreamParameters params; + std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); + + u64 size{}; + auto result = impl.GetWorkBufferSizeForMultiStream(params, size); + + LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); } void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto sample_rate = rp.Pop(); - const auto channel_count = rp.Pop(); - LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); + auto params = rp.PopRaw(); + auto transfer_memory_size{rp.Pop()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ + system.ApplicationProcess()->GetHandleTable().GetObject( + transfer_memory_handle)}; - ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || - sample_rate == 12000 || sample_rate == 8000, - "Invalid sample rate"); - ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", + params.sample_rate, params.channel_count, transfer_memory_size); - const int num_stereo_streams = channel_count == 2 ? 1 : 0; - const auto mapping_table = CreateMappingTable(channel_count); + auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; - int error = 0; - OpusDecoderPtr decoder{ - opus_multistream_decoder_create(sample_rate, static_cast(channel_count), 1, - num_stereo_streams, mapping_table.data(), &error)}; - if (error != OPUS_OK || decoder == nullptr) { - LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(ogniK): Use correct error code - rb.Push(ResultUnknown); - return; - } + auto result = + decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface( - system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); + rb.Push(result); + rb.PushIpcInterface(decoder); +} + +void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto params = rp.PopRaw(); + + u64 size{}; + auto result = impl.GetWorkBufferSizeEx(params, size); + + LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); } void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input{ctx.ReadBuffer()}; OpusMultiStreamParametersEx params; - std::memcpy(¶ms, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); + std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); - const auto& sample_rate = params.sample_rate; - const auto& channel_count = params.channel_count; + auto transfer_memory_size{rp.Pop()}; + auto transfer_memory_handle{ctx.GetCopyHandle(0)}; + auto transfer_memory{ + system.ApplicationProcess()->GetHandleTable().GetObject( + transfer_memory_handle)}; - LOG_INFO( - Audio, - "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", - sample_rate, channel_count, params.number_streams, params.number_stereo_streams); + LOG_DEBUG(Service_Audio, + "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " + "use_large_frame_size {}" + "transfer_memory_size 0x{:X}", + params.sample_rate, params.channel_count, params.total_stream_count, + params.stereo_stream_count, params.use_large_frame_size, transfer_memory_size); - ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || - sample_rate == 12000 || sample_rate == 8000, - "Invalid sample rate"); + auto decoder{std::make_shared(system, impl.GetHardwareOpus())}; - int error = 0; - OpusDecoderPtr decoder{opus_multistream_decoder_create( - sample_rate, static_cast(channel_count), params.number_streams, - params.number_stereo_streams, params.channel_mappings.data(), &error)}; - if (error != OPUS_OK || decoder == nullptr) { - LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(ogniK): Use correct error code - rb.Push(ResultUnknown); - return; - } + auto result = + decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface( - system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); + rb.Push(result); + rb.PushIpcInterface(decoder); } -HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { +void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input{ctx.ReadBuffer()}; + OpusMultiStreamParametersEx params; + std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); + + u64 size{}; + auto result = impl.GetWorkBufferSizeForMultiStreamEx(params, size); + + LOG_DEBUG(Service_Audio, + "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " + "use_large_frame_size {} -- returned size 0x{:X}", + params.sample_rate, params.channel_count, params.total_stream_count, + params.stereo_stream_count, params.use_large_frame_size, size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); +} + +void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto params = rp.PopRaw(); + + u64 size{}; + auto result = impl.GetWorkBufferSizeExEx(params, size); + + LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); +} + +void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto input{ctx.ReadBuffer()}; + OpusMultiStreamParametersEx params; + std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); + + u64 size{}; + auto result = impl.GetWorkBufferSizeForMultiStreamExEx(params, size); + + LOG_DEBUG(Service_Audio, "size 0x{:X}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(size); +} + +HwOpus::HwOpus(Core::System& system_) + : ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} { static const FunctionInfo functions[] = { {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, - {2, nullptr, "OpenOpusDecoderForMultiStream"}, - {3, nullptr, "GetWorkBufferSizeForMultiStream"}, + {2, &HwOpus::OpenHardwareOpusDecoderForMultiStream, "OpenOpusDecoderForMultiStream"}, + {3, &HwOpus::GetWorkBufferSizeForMultiStream, "GetWorkBufferSizeForMultiStream"}, {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx, "OpenHardwareOpusDecoderForMultiStreamEx"}, {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, - {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, + {9, &HwOpus::GetWorkBufferSizeForMultiStreamExEx, "GetWorkBufferSizeForMultiStreamExEx"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 90867bf74d..d3960065e7 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h @@ -3,6 +3,7 @@ #pragma once +#include "audio_core/opus/decoder_manager.h" #include "core/hle/service/service.h" namespace Core { @@ -11,18 +12,6 @@ class System; namespace Service::Audio { -struct OpusMultiStreamParametersEx { - u32 sample_rate; - u32 channel_count; - u32 number_streams; - u32 number_stereo_streams; - u32 use_large_frame_size; - u32 padding; - std::array channel_mappings; -}; -static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, - "OpusMultiStreamParametersEx has incorrect size"); - class HwOpus final : public ServiceFramework { public: explicit HwOpus(Core::System& system_); @@ -30,12 +19,18 @@ public: private: void OpenHardwareOpusDecoder(HLERequestContext& ctx); - void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); - void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx); void GetWorkBufferSize(HLERequestContext& ctx); + void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx); + void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx); + void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); void GetWorkBufferSizeEx(HLERequestContext& ctx); - void GetWorkBufferSizeExEx(HLERequestContext& ctx); + void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx); void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); + void GetWorkBufferSizeExEx(HLERequestContext& ctx); + void GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx); + + Core::System& system; + AudioCore::OpusDecoder::OpusDecoderManager impl; }; } // namespace Service::Audio From c484a61515cc9d0dc6cad9396d26b149e1e135ae Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sat, 16 Sep 2023 17:15:20 -0400 Subject: [PATCH 65/69] android: Collect task state once view is created Before the viewLifecycleOwner wasn't ready and would cause a crash --- .../IndeterminateProgressDialogFragment.kt | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) 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 ea8eb073ac..18bc34b9fa 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 @@ -5,6 +5,9 @@ package org.yuzu.yuzu_emu.fragments import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment @@ -21,22 +24,40 @@ import org.yuzu.yuzu_emu.model.TaskViewModel class IndeterminateProgressDialogFragment : DialogFragment() { private val taskViewModel: TaskViewModel by activityViewModels() + private lateinit var binding: DialogProgressBarBinding + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val titleId = requireArguments().getInt(TITLE) - val progressBinding = DialogProgressBarBinding.inflate(layoutInflater) - progressBinding.progressBar.isIndeterminate = true + binding = DialogProgressBarBinding.inflate(layoutInflater) + binding.progressBar.isIndeterminate = true val dialog = MaterialAlertDialogBuilder(requireContext()) .setTitle(titleId) - .setView(progressBinding.root) + .setView(binding.root) .create() dialog.setCanceledOnTouchOutside(false) + if (!taskViewModel.isRunning.value) { + taskViewModel.runTask() + } + return dialog + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.CREATED) { taskViewModel.isComplete.collect { if (it) { - dialog.dismiss() + dismiss() when (val result = taskViewModel.result.value) { is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG) .show() @@ -51,11 +72,6 @@ class IndeterminateProgressDialogFragment : DialogFragment() { } } } - - if (!taskViewModel.isRunning.value) { - taskViewModel.runTask() - } - return dialog } companion object { From 13a7a297bb0c56335e83322c8b9ce0e257cd6ff8 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 16 Sep 2023 18:25:17 -0400 Subject: [PATCH 66/69] registered_cache: correct file deletion case --- src/core/file_sys/registered_cache.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index e33b00d89f..04da93d5cd 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -752,7 +752,9 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { for (u8 i = 0; i < 0x10; i++) { const auto meta_dir = dir->CreateDirectoryRelative("yuzu_meta"); const auto filename = GetCNMTName(TitleType::Update, title_id + i); - removed_data |= meta_dir->DeleteFile(filename); + if (meta_dir->GetFile(filename)) { + removed_data |= meta_dir->DeleteFile(filename); + } } return removed_data; From af0c1b0cb7caa079fe746926ff93643e3b61bc89 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sat, 16 Sep 2023 20:40:32 -0400 Subject: [PATCH 67/69] android: Use 1 worker for shader compilation for all devices --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 4f83a88e18..a1ec1a1002 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -294,10 +294,11 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, - workers(device.GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY - ? 1 - : (std::max(std::thread::hardware_concurrency(), 2U) - 1), - "VkPipelineBuilder"), +#ifdef ANDROID + workers(1, "VkPipelineBuilder"), +#else + workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), +#endif serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; const VkDriverId driver_id{device.GetDriverID()}; From 67eeb05692a96828eed87af7163868b22ac0553d Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sun, 17 Sep 2023 01:22:16 -0400 Subject: [PATCH 68/69] android: Set up signing config for release builds --- src/android/app/build.gradle.kts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index fe79a701ce..431f899b3b 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -77,13 +77,30 @@ android { buildConfigField("String", "BRANCH", "\"${getBranch()}\"") } + val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE") + if (keystoreFile != null) { + signingConfigs { + create("release") { + storeFile = file(keystoreFile) + storePassword = System.getenv("ANDROID_KEYSTORE_PASS") + keyAlias = System.getenv("ANDROID_KEY_ALIAS") + keyPassword = System.getenv("ANDROID_KEYSTORE_PASS") + } + } + } + // Define build types, which are orthogonal to product flavors. buildTypes { // Signed by release key, allowing for upload to Play Store. release { + signingConfig = if (keystoreFile != null) { + signingConfigs.getByName("release") + } else { + signingConfigs.getByName("debug") + } + resValue("string", "app_name_suffixed", "yuzu") - signingConfig = signingConfigs.getByName("debug") isMinifyEnabled = true isDebuggable = false proguardFiles( From 77682aabd3a8c4dbda3dd20421a5dea16b6ef86d Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Sun, 17 Sep 2023 15:23:57 -0400 Subject: [PATCH 69/69] mii_types: Remove null terminator check This is an OoB array access, causing a crash on at least the Linux Flatpak releases. Co-authored-by: german77 --- src/core/hle/service/mii/mii_types.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h index 95476f7456..611ff4f81a 100644 --- a/src/core/hle/service/mii/mii_types.h +++ b/src/core/hle/service/mii/mii_types.h @@ -606,15 +606,12 @@ struct Nickname { static constexpr std::size_t MaxNameSize = 10; std::array data; - // Checks for null, non-zero terminated or dirty strings + // Checks for null 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++;