diff options
Diffstat (limited to 'src')
266 files changed, 6840 insertions, 3788 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6068c7a1f..d7f68618c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ if (MSVC) # Ensure that projects build with Unicode support. add_definitions(-DUNICODE -D_UNICODE) - # /W3 - Level 3 warnings + # /W4 - Level 4 warnings # /MP - Multi-threaded compilation # /Zi - Output debugging information # /Zm - Specifies the precompiled header memory allocation limit @@ -61,7 +61,7 @@ if (MSVC) /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers # Warnings - /W3 + /W4 /WX /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled @@ -84,12 +84,17 @@ 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) # when caching, we need to use /Z7 to downgrade debug info to use an older but more cacheable format # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21 add_compile_options(/Z7) + # Avoid D9025 warning + string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") else() add_compile_options(/Zi) endif() @@ -105,6 +110,8 @@ if (MSVC) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) else() add_compile_options( + -fwrapv + -Werror=all -Werror=extra -Werror=missing-declarations @@ -129,7 +136,6 @@ else() if (ARCHITECTURE_x86_64) add_compile_options("-mcx16") - add_compile_options("-fwrapv") endif() if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 0f2a6d9e4..b9ecefa74 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<SoftwareKeyboard::AndroidKeyboard>(); diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index e7b595459..67dfe0290 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -2,6 +2,14 @@ # SPDX-License-Identifier: GPL-2.0-or-later add_library(audio_core STATIC + adsp/adsp.cpp + adsp/adsp.h + adsp/mailbox.h + adsp/apps/audio_renderer/audio_renderer.cpp + adsp/apps/audio_renderer/audio_renderer.h + adsp/apps/audio_renderer/command_buffer.h + adsp/apps/audio_renderer/command_list_processor.cpp + adsp/apps/audio_renderer/command_list_processor.h audio_core.cpp audio_core.h audio_event.h @@ -32,13 +40,6 @@ add_library(audio_core STATIC out/audio_out_system.cpp out/audio_out_system.h precompiled_headers.h - renderer/adsp/adsp.cpp - renderer/adsp/adsp.h - renderer/adsp/audio_renderer.cpp - renderer/adsp/audio_renderer.h - renderer/adsp/command_buffer.h - renderer/adsp/command_list_processor.cpp - renderer/adsp/command_list_processor.h renderer/audio_device.cpp renderer/audio_device.h renderer/audio_renderer.h diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp new file mode 100644 index 000000000..0580990f5 --- /dev/null +++ b/src/audio_core/adsp/adsp.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/adsp.h" +#include "core/core.h" + +namespace AudioCore::ADSP { + +ADSP::ADSP(Core::System& system, Sink::Sink& sink) { + audio_renderer = + std::make_unique<AudioRenderer::AudioRenderer>(system, system.ApplicationMemory(), sink); +} + +AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { + return *audio_renderer.get(); +} + +} // namespace AudioCore::ADSP diff --git a/src/audio_core/adsp/adsp.h b/src/audio_core/adsp/adsp.h new file mode 100644 index 000000000..bd5bcc63b --- /dev/null +++ b/src/audio_core/adsp/adsp.h @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" +#include "common/common_types.h" + +namespace Core { +class System; +} // namespace Core + +namespace AudioCore { +namespace Sink { +class Sink; +} + +namespace ADSP { + +/** + * Represents the ADSP embedded within the audio sysmodule. + * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot. + * + * The kernel will run the apps you write for it, Nintendo have the following: + * + * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all + * audio samples end up, and we skip it entirely, since we have very different backends and + * mixing is implicitly handled by the OS (but also due to lack of research/simplicity). + * + * AudioRenderer - Receives command lists generated by the audio render + * system on the host, processes them, and sends the samples to Gmix. + * + * OpusDecoder - Contains libopus, and decodes Opus audio packets into raw pcm data. + * + * Communication between the host and ADSP is done through mailboxes, and mapping of shared memory. + */ +class ADSP { +public: + explicit ADSP(Core::System& system, Sink::Sink& sink); + ~ADSP() = default; + + AudioRenderer::AudioRenderer& AudioRenderer(); + +private: + /// AudioRenderer app + std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{}; +}; + +} // namespace ADSP +} // namespace AudioCore diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp new file mode 100644 index 000000000..2e549bc6f --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp @@ -0,0 +1,220 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <array> +#include <chrono> + +#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" +#include "audio_core/audio_core.h" +#include "audio_core/common/common.h" +#include "audio_core/sink/sink.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(Audio_Renderer, "Audio", "DSP", 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() { + Stop(); +} + +void AudioRenderer::Start() { + CreateSinkStreams(); + + mailbox.Initialize(AppMailboxId::AudioRenderer); + + 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) { + LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " + "message response from ADSP!"); + return; + } + running = true; +} + +void AudioRenderer::Stop() { + if (!running) { + return; + } + + mailbox.Send(Direction::DSP, {Message::Shutdown, {}}); + if (mailbox.Receive(Direction::Host).msg != Message::Shutdown) { + LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " + "message response from ADSP!"); + } + main_thread.request_stop(); + main_thread.join(); + + for (auto& stream : streams) { + if (stream) { + stream->Stop(); + sink.CloseStream(stream); + stream = nullptr; + } + } + running = false; +} + +void AudioRenderer::Signal() { + signalled_tick = system.CoreTiming().GetGlobalTimeNs().count(); + Send(Direction::DSP, {Message::Render, {}}); +} + +void AudioRenderer::Wait() { + auto received = Receive(Direction::Host); + if (received.msg != Message::RenderResponse) { + LOG_ERROR(Service_Audio, + "Did not receive the expected render response from the AudioRenderer! Expected " + "{}, got {}", + Message::RenderResponse, received.msg); + } +} + +void AudioRenderer::Send(Direction dir, MailboxMessage message) { + mailbox.Send(dir, std::move(message)); +} + +MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { + return mailbox.Receive(dir, block); +} + +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 { + return command_buffers[session_id].remaining_command_count; +} + +void AudioRenderer::ClearRemainCommandCount(s32 session_id) noexcept { + command_buffers[session_id].remaining_command_count = 0; +} + +u64 AudioRenderer::GetRenderingStartTick(s32 session_id) const noexcept { + return (1000 * command_buffers[session_id].render_time_taken_us) + signalled_tick; +} + +void AudioRenderer::CreateSinkStreams() { + u32 channels{sink.GetDeviceChannels()}; + for (u32 i = 0; i < MaxRendererSessions; i++) { + std::string name{fmt::format("ADSP_RenderStream-{}", i)}; + streams[i] = + sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); + streams[i]->SetRingSize(4); + } +} + +void AudioRenderer::Main(std::stop_token stop_token) { + static constexpr char name[]{"AudioRenderer"}; + MicroProfileOnThreadCreate(name); + Common::SetCurrentThreadName(name); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); + + // TODO: Create buffer map/unmap thread + mailbox + // TODO: Create gMix devices, initialize them here + + if (mailbox.Receive(Direction::DSP).msg != Message::InitializeOK) { + LOG_ERROR(Service_Audio, + "ADSP Audio Renderer -- Failed to receive initialize message from host!"); + return; + } + + 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) { + case 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, {}}); + continue; + } + std::array<bool, MaxRendererSessions> buffers_reset{}; + std::array<u64, MaxRendererSessions> render_times_taken{}; + const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()}; + + for (u32 index = 0; index < MaxRendererSessions; index++) { + auto& command_buffer{command_buffers[index]}; + auto& command_list_processor{command_list_processors[index]}; + + // Check this buffer is valid, as it may not be used. + if (command_buffer.buffer != 0) { + // If there are no remaining commands (from the previous list), + // this is a new command list, initialize it. + if (command_buffer.remaining_command_count == 0) { + command_list_processor.Initialize(system, command_buffer.buffer, + command_buffer.size, streams[index]); + } + + if (command_buffer.reset_buffer && !buffers_reset[index]) { + streams[index]->ClearQueue(); + buffers_reset[index] = true; + } + + u64 max_time{max_process_time}; + if (index == 1 && command_buffer.applet_resource_user_id == + command_buffers[0].applet_resource_user_id) { + max_time = max_process_time - render_times_taken[0]; + if (render_times_taken[0] > max_process_time) { + max_time = 0; + } + } + + max_time = std::min(command_buffer.time_limit, max_time); + command_list_processor.SetProcessTimeMax(max_time); + + if (index == 0) { + streams[index]->WaitFreeSpace(stop_token); + } + + // Process the command list + { + MICROPROFILE_SCOPE(Audio_Renderer); + render_times_taken[index] = + command_list_processor.Process(index) - start_time; + } + + const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()}; + + command_buffer.remaining_command_count = + command_list_processor.GetRemainingCommandCount(); + command_buffer.render_time_taken_us = end_time - start_time; + } + } + + mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); + } break; + + default: + LOG_WARNING(Service_Audio, + "ADSP AudioRenderer received an invalid message, msg={:02X}!", + received.msg); + break; + } + } +} + +} // namespace AudioCore::ADSP::AudioRenderer diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h new file mode 100644 index 000000000..3f5b7dca2 --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <memory> +#include <thread> + +#include "audio_core/adsp/apps/audio_renderer/command_buffer.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" +#include "audio_core/adsp/mailbox.h" +#include "common/common_types.h" +#include "common/polyfill_thread.h" +#include "common/reader_writer_queue.h" +#include "common/thread.h" + +namespace Core { +class System; +namespace Timing { +struct EventType; +} +namespace Memory { +class Memory; +} +class System; +} // namespace Core + +namespace AudioCore { +namespace Sink { +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, +}; + +/** + * The AudioRenderer application running on the ADSP. + */ +class AudioRenderer { +public: + explicit AudioRenderer(Core::System& system, Core::Memory::Memory& memory, Sink::Sink& sink); + ~AudioRenderer(); + + /** + * Start the AudioRenderer. + * + * @param mailbox The mailbox to use for this session. + */ + void Start(); + + /** + * Stop the AudioRenderer. + */ + void Stop(); + + void Signal(); + void Wait(); + + void Send(Direction dir, MailboxMessage message); + MailboxMessage Receive(Direction dir, bool block = true); + + 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; + +private: + /** + * Main AudioRenderer thread, responsible for processing the command lists. + */ + void Main(std::stop_token stop_token); + + /** + * Creates the streams which will receive the processed samples. + */ + void CreateSinkStreams(); + + /// Core system + Core::System& system; + /// Memory + Core::Memory::Memory& memory; + /// The output sink the AudioRenderer will use + Sink::Sink& sink; + /// The active mailbox + Mailbox mailbox; + /// Main thread + std::jthread main_thread{}; + /// The current state + std::atomic<bool> running{}; + std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; + /// The command lists to process + std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; + /// The streams which will receive the processed samples + std::array<Sink::SinkStream*, MaxRendererSessions> streams{}; + u64 signalled_tick{0}; +}; + +} // namespace ADSP::AudioRenderer +} // namespace AudioCore diff --git a/src/audio_core/adsp/apps/audio_renderer/command_buffer.h b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h new file mode 100644 index 000000000..3fd1b09dc --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/common/common.h" +#include "common/common_types.h" + +namespace AudioCore::ADSP::AudioRenderer { + +struct CommandBuffer { + // Set by the host + CpuAddr buffer{}; + u64 size{}; + u64 time_limit{}; + u64 applet_resource_user_id{}; + bool reset_buffer{}; + // Set by the DSP + u32 remaining_command_count{}; + u64 render_time_taken_us{}; +}; + +} // namespace AudioCore::ADSP::AudioRenderer diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp index 3a0f1ae38..24e4d0496 100644 --- a/src/audio_core/renderer/adsp/command_list_processor.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp @@ -1,9 +1,9 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include <string> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/command_list_header.h" #include "audio_core/renderer/command/commands.h" #include "common/settings.h" @@ -11,15 +11,15 @@ #include "core/core_timing.h" #include "core/memory.h" -namespace AudioCore::AudioRenderer::ADSP { +namespace AudioCore::ADSP::AudioRenderer { void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size, Sink::SinkStream* stream_) { system = &system_; memory = &system->ApplicationMemory(); stream = stream_; - header = reinterpret_cast<CommandListHeader*>(buffer); - commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader)); + header = reinterpret_cast<Renderer::CommandListHeader*>(buffer); + commands = reinterpret_cast<u8*>(buffer + sizeof(Renderer::CommandListHeader)); commands_buffer_size = size; command_count = header->command_count; sample_count = header->sample_count; @@ -37,17 +37,12 @@ u32 CommandListProcessor::GetRemainingCommandCount() const { return command_count - processed_command_count; } -void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) { - commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader)); - commands_buffer_size = size; -} - Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const { return stream; } u64 CommandListProcessor::Process(u32 session_id) { - const auto start_time_{system->CoreTiming().GetClockTicks()}; + const auto start_time_{system->CoreTiming().GetGlobalTimeUs().count()}; const auto command_base{CpuAddr(commands)}; if (processed_command_count > 0) { @@ -60,12 +55,12 @@ u64 CommandListProcessor::Process(u32 session_id) { std::string dump{fmt::format("\nSession {}\n", session_id)}; for (u32 index = 0; index < command_count; index++) { - auto& command{*reinterpret_cast<ICommand*>(commands)}; + auto& command{*reinterpret_cast<Renderer::ICommand*>(commands)}; if (command.magic != 0xCAFEBABE) { LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}", command.magic); - return system->CoreTiming().GetClockTicks() - start_time_; + return system->CoreTiming().GetGlobalTimeUs().count() - start_time_; } auto current_offset{CpuAddr(commands) - command_base}; @@ -74,8 +69,8 @@ u64 CommandListProcessor::Process(u32 session_id) { LOG_ERROR(Service_Audio, "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}", commands_buffer_size, - CpuAddr(commands) + command.size - sizeof(CommandListHeader)); - return system->CoreTiming().GetClockTicks() - start_time_; + CpuAddr(commands) + command.size - sizeof(Renderer::CommandListHeader)); + return system->CoreTiming().GetGlobalTimeUs().count() - start_time_; } if (Settings::values.dump_audio_commands) { @@ -101,8 +96,8 @@ u64 CommandListProcessor::Process(u32 session_id) { last_dump = dump; } - end_time = system->CoreTiming().GetClockTicks(); + end_time = system->CoreTiming().GetGlobalTimeUs().count(); return end_time - start_time_; } -} // namespace AudioCore::AudioRenderer::ADSP +} // namespace AudioCore::ADSP::AudioRenderer diff --git a/src/audio_core/renderer/adsp/command_list_processor.h b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h index d78269e1d..4e5fb793e 100644 --- a/src/audio_core/renderer/adsp/command_list_processor.h +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -6,6 +6,7 @@ #include <span> #include "audio_core/common/common.h" +#include "audio_core/renderer/command/command_list_header.h" #include "common/common_types.h" namespace Core { @@ -20,10 +21,11 @@ namespace Sink { class SinkStream; } -namespace AudioRenderer { +namespace Renderer { struct CommandListHeader; +} -namespace ADSP { +namespace ADSP::AudioRenderer { /** * A processor for command lists given to the AudioRenderer. @@ -55,14 +57,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. * * @return The stream associated with this command list. @@ -85,7 +79,7 @@ public: /// Stream for the processed samples Sink::SinkStream* stream{}; /// Header info for this command list - CommandListHeader* header{}; + Renderer::CommandListHeader* header{}; /// The command buffer u8* commands{}; /// The command buffer size @@ -114,6 +108,5 @@ public: std::string last_dump{}; }; -} // namespace ADSP -} // namespace AudioRenderer +} // namespace ADSP::AudioRenderer } // namespace AudioCore diff --git a/src/audio_core/adsp/mailbox.h b/src/audio_core/adsp/mailbox.h new file mode 100644 index 000000000..c31b73717 --- /dev/null +++ b/src/audio_core/adsp/mailbox.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/bounded_threadsafe_queue.h" +#include "common/common_types.h" + +namespace AudioCore::ADSP { + +enum class AppMailboxId : u32 { + Invalid = 0, + AudioRenderer = 50, + AudioRendererMemoryMapUnmap = 51, +}; + +enum class Direction : u32 { + Host, + DSP, +}; + +struct MailboxMessage { + u32 msg; + std::span<u8> data; +}; + +class Mailbox { +public: + void Initialize(AppMailboxId id_) { + Reset(); + id = id_; + } + + AppMailboxId Id() const noexcept { + return id; + } + + void Send(Direction dir, MailboxMessage&& message) { + auto& queue = dir == Direction::Host ? host_queue : adsp_queue; + queue.EmplaceWait(std::move(message)); + } + + MailboxMessage Receive(Direction dir, bool block = true) { + auto& queue = dir == Direction::Host ? host_queue : adsp_queue; + MailboxMessage t; + if (block) { + queue.PopWait(t); + } else { + queue.TryPop(t); + } + return t; + } + + void Reset() { + id = AppMailboxId::Invalid; + MailboxMessage t; + while (host_queue.TryPop(t)) { + } + while (adsp_queue.TryPop(t)) { + } + } + +private: + AppMailboxId id{0}; + Common::SPSCQueue<MailboxMessage> host_queue; + Common::SPSCQueue<MailboxMessage> adsp_queue; +}; + +} // namespace AudioCore::ADSP diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 703ef4494..fcaab2b32 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -11,7 +11,7 @@ namespace AudioCore { AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} { CreateSinks(); // Must be created after the sinks - adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink); + adsp = std::make_unique<ADSP::ADSP>(system, *output_sink); } AudioCore ::~AudioCore() { @@ -43,7 +43,7 @@ Sink::Sink& AudioCore::GetInputSink() { return *input_sink; } -AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() { +ADSP::ADSP& AudioCore::ADSP() { return *adsp; } diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index ea047773e..e4e27fc66 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -5,8 +5,8 @@ #include <memory> +#include "audio_core/adsp/adsp.h" #include "audio_core/audio_manager.h" -#include "audio_core/renderer/adsp/adsp.h" #include "audio_core/sink/sink.h" namespace Core { @@ -55,7 +55,7 @@ public: * * @return Ref to the ADSP. */ - AudioRenderer::ADSP::ADSP& GetADSP(); + ADSP::ADSP& ADSP(); private: /** @@ -70,7 +70,7 @@ private: /// Sink used for audio input std::unique_ptr<Sink::Sink> input_sink; /// The ADSP in the sysmodule - std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp; + std::unique_ptr<ADSP::ADSP> adsp; }; } // namespace AudioCore diff --git a/src/audio_core/audio_event.cpp b/src/audio_core/audio_event.cpp index d15568e1f..c23ef0990 100644 --- a/src/audio_core/audio_event.cpp +++ b/src/audio_core/audio_event.cpp @@ -20,7 +20,6 @@ size_t Event::GetManagerIndex(const Type type) const { default: UNREACHABLE(); } - return 3; } void Event::SetAudioEvent(const Type type, const bool signalled) { diff --git a/src/audio_core/audio_in_manager.cpp b/src/audio_core/audio_in_manager.cpp index 3dfb613cb..a3667524f 100644 --- a/src/audio_core/audio_in_manager.cpp +++ b/src/audio_core/audio_in_manager.cpp @@ -73,7 +73,7 @@ void Manager::BufferReleaseAndRegister() { } } -u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, +u32 Manager::GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, [[maybe_unused]] const u32 max_count, [[maybe_unused]] const bool filter) { std::scoped_lock l{mutex}; diff --git a/src/audio_core/audio_in_manager.h b/src/audio_core/audio_in_manager.h index 8a519df99..5c4614cd1 100644 --- a/src/audio_core/audio_in_manager.h +++ b/src/audio_core/audio_in_manager.h @@ -65,8 +65,8 @@ public: * * @return Number of names written. */ - u32 GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, - u32 max_count, bool filter); + u32 GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, u32 max_count, + bool filter); /// Core system Core::System& system; diff --git a/src/audio_core/audio_out_manager.cpp b/src/audio_core/audio_out_manager.cpp index f22821360..316ea7c81 100644 --- a/src/audio_core/audio_out_manager.cpp +++ b/src/audio_core/audio_out_manager.cpp @@ -73,7 +73,7 @@ void Manager::BufferReleaseAndRegister() { } u32 Manager::GetAudioOutDeviceNames( - std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const { + std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const { names.emplace_back("DeviceOut"); return 1; } diff --git a/src/audio_core/audio_out_manager.h b/src/audio_core/audio_out_manager.h index 1e05ec5ed..c3e445d5d 100644 --- a/src/audio_core/audio_out_manager.h +++ b/src/audio_core/audio_out_manager.h @@ -61,8 +61,7 @@ public: * @param names - Output container to write names to. * @return Number of names written. */ - u32 GetAudioOutDeviceNames( - std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const; + u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const; /// Core system Core::System& system; diff --git a/src/audio_core/audio_render_manager.cpp b/src/audio_core/audio_render_manager.cpp index 320715727..3c53e3afd 100644 --- a/src/audio_core/audio_render_manager.cpp +++ b/src/audio_core/audio_render_manager.cpp @@ -6,7 +6,7 @@ #include "audio_core/common/feature_support.h" #include "core/core.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { Manager::Manager(Core::System& system_) : system{system_}, system_manager{std::make_unique<SystemManager>(system)} { @@ -67,4 +67,4 @@ bool Manager::RemoveSystem(System& system_) { return system_manager->Remove(system_); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h index fffa5944d..45537b270 100644 --- a/src/audio_core/audio_render_manager.h +++ b/src/audio_core/audio_render_manager.h @@ -20,7 +20,7 @@ class System; namespace AudioCore { struct AudioRendererParameterInternal; -namespace AudioRenderer { +namespace Renderer { /** * Wrapper for the audio system manager, handles service calls. */ @@ -101,5 +101,5 @@ private: std::unique_ptr<SystemManager> system_manager{}; }; -} // namespace AudioRenderer +} // namespace Renderer } // namespace AudioCore diff --git a/src/audio_core/common/audio_renderer_parameter.h b/src/audio_core/common/audio_renderer_parameter.h index 8c7892bcf..6c4e9fdc6 100644 --- a/src/audio_core/common/audio_renderer_parameter.h +++ b/src/audio_core/common/audio_renderer_parameter.h @@ -51,10 +51,10 @@ struct AudioRendererSystemContext { s32 session_id; s8 channels; s16 mix_buffer_count; - AudioRenderer::BehaviorInfo* behavior; + Renderer::BehaviorInfo* behavior; std::span<s32> depop_buffer; - AudioRenderer::UpsamplerManager* upsampler_manager; - AudioRenderer::MemoryPoolInfo* memory_pool_info; + Renderer::UpsamplerManager* upsampler_manager; + Renderer::MemoryPoolInfo* memory_pool_info; }; } // namespace AudioCore diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp deleted file mode 100644 index b1db31e93..000000000 --- a/src/audio_core/renderer/adsp/adsp.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "audio_core/renderer/adsp/adsp.h" -#include "audio_core/renderer/adsp/command_buffer.h" -#include "audio_core/sink/sink.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/memory.h" - -namespace AudioCore::AudioRenderer::ADSP { - -ADSP::ADSP(Core::System& system_, Sink::Sink& sink_) - : system{system_}, memory{system.ApplicationMemory()}, sink{sink_} {} - -ADSP::~ADSP() { - ClearCommandBuffers(); -} - -State ADSP::GetState() const { - if (running) { - return State::Started; - } - return State::Stopped; -} - -AudioRenderer_Mailbox* ADSP::GetRenderMailbox() { - return &render_mailbox; -} - -void ADSP::ClearRemainCount(const u32 session_id) { - render_mailbox.ClearRemainCount(session_id); -} - -u64 ADSP::GetSignalledTick() const { - return render_mailbox.GetSignalledTick(); -} - -u64 ADSP::GetTimeTaken() const { - return render_mailbox.GetRenderTimeTaken(); -} - -u64 ADSP::GetRenderTimeTaken(const u32 session_id) { - return render_mailbox.GetCommandBuffer(session_id).render_time_taken; -} - -u32 ADSP::GetRemainCommandCount(const u32 session_id) const { - return render_mailbox.GetRemainCommandCount(session_id); -} - -void ADSP::SendCommandBuffer(const u32 session_id, const CommandBuffer& command_buffer) { - render_mailbox.SetCommandBuffer(session_id, command_buffer); -} - -u64 ADSP::GetRenderingStartTick(const u32 session_id) { - return render_mailbox.GetSignalledTick() + - render_mailbox.GetCommandBuffer(session_id).render_time_taken; -} - -bool ADSP::Start() { - if (running) { - return running; - } - - running = true; - systems_active++; - audio_renderer = std::make_unique<AudioRenderer>(system); - audio_renderer->Start(&render_mailbox); - render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_InitializeOK); - if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) { - LOG_ERROR( - Service_Audio, - "Host Audio Renderer -- Failed to receive initialize message response from ADSP!"); - } - return running; -} - -void ADSP::Stop() { - systems_active--; - if (running && systems_active == 0) { - { - std::scoped_lock l{mailbox_lock}; - render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Shutdown); - if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_Shutdown) { - LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " - "message response from ADSP!"); - } - } - audio_renderer->Stop(); - running = false; - } -} - -void ADSP::Signal() { - const auto signalled_tick{system.CoreTiming().GetClockTicks()}; - render_mailbox.SetSignalledTick(signalled_tick); - render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Render); -} - -void ADSP::Wait() { - std::scoped_lock l{mailbox_lock}; - auto response{render_mailbox.HostWaitMessage()}; - if (response != RenderMessage::AudioRenderer_RenderResponse) { - LOG_ERROR(Service_Audio, "Invalid ADSP response message, expected 0x{:02X}, got 0x{:02X}", - static_cast<u32>(RenderMessage::AudioRenderer_RenderResponse), - static_cast<u32>(response)); - } - - ClearCommandBuffers(); -} - -void ADSP::ClearCommandBuffers() { - render_mailbox.ClearCommandBuffers(); -} - -} // namespace AudioCore::AudioRenderer::ADSP diff --git a/src/audio_core/renderer/adsp/adsp.h b/src/audio_core/renderer/adsp/adsp.h deleted file mode 100644 index f7a2f25e4..000000000 --- a/src/audio_core/renderer/adsp/adsp.h +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <mutex> - -#include "audio_core/renderer/adsp/audio_renderer.h" -#include "common/common_types.h" - -namespace Core { -namespace Memory { -class Memory; -} -class System; -} // namespace Core - -namespace AudioCore { -namespace Sink { -class Sink; -} - -namespace AudioRenderer::ADSP { -struct CommandBuffer; - -enum class State { - Started, - Stopped, -}; - -/** - * Represents the ADSP embedded within the audio sysmodule. - * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot. - * - * The kernel will run apps you program for it, Nintendo have the following: - * - * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all - * audio samples end up, and we skip it entirely, since we have very different backends and - * mixing is implicitly handled by the OS (but also due to lack of research/simplicity). - * - * AudioRenderer - Receives command lists generated by the audio render - * system, processes them, and sends the samples to Gmix. - * - * OpusDecoder - Contains libopus, and controls processing Opus audio and sends it to Gmix. - * Not much research done here, TODO if needed. - * - * We only implement the AudioRenderer for now. - * - * Communication for the apps is done through mailboxes, and some shared memory. - */ -class ADSP { -public: - explicit ADSP(Core::System& system, Sink::Sink& sink); - ~ADSP(); - - /** - * Start the ADSP. - * - * @return True if started or already running, otherwise false. - */ - bool Start(); - - /** - * Stop the ADSP. - */ - void Stop(); - - /** - * Get the ADSP's state. - * - * @return Started or Stopped. - */ - State GetState() const; - - /** - * Get the AudioRenderer mailbox to communicate with it. - * - * @return The AudioRenderer mailbox. - */ - AudioRenderer_Mailbox* GetRenderMailbox(); - - /** - * Get the tick the ADSP was signalled. - * - * @return The tick the ADSP was signalled. - */ - u64 GetSignalledTick() const; - - /** - * Get the total time it took for the ADSP to run the last command lists (both command lists). - * - * @return The tick the ADSP was signalled. - */ - u64 GetTimeTaken() const; - - /** - * Get the last time a given command list took to run. - * - * @param session_id - The session id to check (0 or 1). - * @return The time it took. - */ - u64 GetRenderTimeTaken(u32 session_id); - - /** - * Clear the remaining command count for a given session. - * - * @param session_id - The session id to check (0 or 1). - */ - void ClearRemainCount(u32 session_id); - - /** - * Get the remaining number of commands left to process for a command list. - * - * @param session_id - The session id to check (0 or 1). - * @return The number of commands remaining. - */ - u32 GetRemainCommandCount(u32 session_id) const; - - /** - * Get the last tick a command list started processing. - * - * @param session_id - The session id to check (0 or 1). - * @return The last tick the given command list started. - */ - u64 GetRenderingStartTick(u32 session_id); - - /** - * Set a command buffer to be processed. - * - * @param session_id - The session id to check (0 or 1). - * @param command_buffer - The command buffer to process. - */ - void SendCommandBuffer(u32 session_id, const CommandBuffer& command_buffer); - - /** - * Clear the command buffers (does not clear the time taken or the remaining command count) - */ - void ClearCommandBuffers(); - - /** - * Signal the AudioRenderer to begin processing. - */ - void Signal(); - - /** - * Wait for the AudioRenderer to finish processing. - */ - void Wait(); - -private: - /// Core system - Core::System& system; - /// Core memory - Core::Memory::Memory& memory; - /// Number of systems active, used to prevent accidental shutdowns - u8 systems_active{0}; - /// ADSP running state - std::atomic<bool> running{false}; - /// Output sink used by the ADSP - Sink::Sink& sink; - /// AudioRenderer app - std::unique_ptr<AudioRenderer> audio_renderer{}; - /// Communication for the AudioRenderer - AudioRenderer_Mailbox render_mailbox{}; - /// Mailbox lock ffor the render mailbox - std::mutex mailbox_lock; -}; - -} // namespace AudioRenderer::ADSP -} // namespace AudioCore diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp deleted file mode 100644 index 9ca716b60..000000000 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <array> -#include <chrono> - -#include "audio_core/audio_core.h" -#include "audio_core/common/common.h" -#include "audio_core/renderer/adsp/audio_renderer.h" -#include "audio_core/sink/sink.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(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); - -namespace AudioCore::AudioRenderer::ADSP { - -void AudioRenderer_Mailbox::HostSendMessage(RenderMessage message_) { - adsp_messages.enqueue(message_); - adsp_event.Set(); -} - -RenderMessage AudioRenderer_Mailbox::HostWaitMessage() { - host_event.Wait(); - RenderMessage msg{RenderMessage::Invalid}; - if (!host_messages.try_dequeue(msg)) { - LOG_ERROR(Service_Audio, "Failed to dequeue host message!"); - } - return msg; -} - -void AudioRenderer_Mailbox::ADSPSendMessage(const RenderMessage message_) { - host_messages.enqueue(message_); - host_event.Set(); -} - -RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() { - adsp_event.Wait(); - RenderMessage msg{RenderMessage::Invalid}; - if (!adsp_messages.try_dequeue(msg)) { - LOG_ERROR(Service_Audio, "Failed to dequeue ADSP message!"); - } - return msg; -} - -CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) { - return command_buffers[session_id]; -} - -void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, const CommandBuffer& buffer) { - command_buffers[session_id] = buffer; -} - -u64 AudioRenderer_Mailbox::GetRenderTimeTaken() const { - return command_buffers[0].render_time_taken + command_buffers[1].render_time_taken; -} - -u64 AudioRenderer_Mailbox::GetSignalledTick() const { - return signalled_tick; -} - -void AudioRenderer_Mailbox::SetSignalledTick(const u64 tick) { - signalled_tick = tick; -} - -void AudioRenderer_Mailbox::ClearRemainCount(const u32 session_id) { - command_buffers[session_id].remaining_command_count = 0; -} - -u32 AudioRenderer_Mailbox::GetRemainCommandCount(const u32 session_id) const { - return command_buffers[session_id].remaining_command_count; -} - -void AudioRenderer_Mailbox::ClearCommandBuffers() { - command_buffers[0].buffer = 0; - command_buffers[0].size = 0; - command_buffers[0].reset_buffers = false; - command_buffers[1].buffer = 0; - command_buffers[1].size = 0; - command_buffers[1].reset_buffers = false; -} - -AudioRenderer::AudioRenderer(Core::System& system_) - : system{system_}, sink{system.AudioCore().GetOutputSink()} { - CreateSinkStreams(); -} - -AudioRenderer::~AudioRenderer() { - Stop(); - for (auto& stream : streams) { - if (stream) { - sink.CloseStream(stream); - } - stream = nullptr; - } -} - -void AudioRenderer::Start(AudioRenderer_Mailbox* mailbox_) { - if (running) { - return; - } - - mailbox = mailbox_; - thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); }); - running = true; -} - -void AudioRenderer::Stop() { - if (!running) { - return; - } - - for (auto& stream : streams) { - stream->Stop(); - } - thread.join(); - running = false; -} - -void AudioRenderer::CreateSinkStreams() { - u32 channels{sink.GetDeviceChannels()}; - for (u32 i = 0; i < MaxRendererSessions; i++) { - std::string name{fmt::format("ADSP_RenderStream-{}", i)}; - streams[i] = - sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); - streams[i]->SetRingSize(4); - } -} - -void AudioRenderer::ThreadFunc(std::stop_token stop_token) { - static constexpr char name[]{"AudioRenderer"}; - MicroProfileOnThreadCreate(name); - Common::SetCurrentThreadName(name); - Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - if (mailbox->ADSPWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) { - LOG_ERROR(Service_Audio, - "ADSP Audio Renderer -- Failed to receive initialize message from host!"); - return; - } - - mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK); - - // 0.12 seconds (2304000 / 19200000) - constexpr u64 max_process_time{2'304'000ULL}; - - while (!stop_token.stop_requested()) { - auto message{mailbox->ADSPWaitMessage()}; - switch (message) { - case RenderMessage::AudioRenderer_Shutdown: - mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_Shutdown); - return; - - case RenderMessage::AudioRenderer_Render: { - if (system.IsShuttingDown()) [[unlikely]] { - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse); - continue; - } - std::array<bool, MaxRendererSessions> buffers_reset{}; - std::array<u64, MaxRendererSessions> render_times_taken{}; - const auto start_time{system.CoreTiming().GetClockTicks()}; - - for (u32 index = 0; index < 2; index++) { - auto& command_buffer{mailbox->GetCommandBuffer(index)}; - auto& command_list_processor{command_list_processors[index]}; - - // Check this buffer is valid, as it may not be used. - if (command_buffer.buffer != 0) { - // If there are no remaining commands (from the previous list), - // this is a new command list, initialize it. - if (command_buffer.remaining_command_count == 0) { - command_list_processor.Initialize(system, command_buffer.buffer, - command_buffer.size, streams[index]); - } - - if (command_buffer.reset_buffers && !buffers_reset[index]) { - streams[index]->ClearQueue(); - buffers_reset[index] = true; - } - - u64 max_time{max_process_time}; - if (index == 1 && command_buffer.applet_resource_user_id == - mailbox->GetCommandBuffer(0).applet_resource_user_id) { - max_time = max_process_time - render_times_taken[0]; - if (render_times_taken[0] > max_process_time) { - max_time = 0; - } - } - - max_time = std::min(command_buffer.time_limit, max_time); - command_list_processor.SetProcessTimeMax(max_time); - - streams[index]->WaitFreeSpace(stop_token); - - // Process the command list - { - MICROPROFILE_SCOPE(Audio_Renderer); - render_times_taken[index] = - command_list_processor.Process(index) - start_time; - } - - const auto end_time{system.CoreTiming().GetClockTicks()}; - - command_buffer.remaining_command_count = - command_list_processor.GetRemainingCommandCount(); - command_buffer.render_time_taken = end_time - start_time; - } - } - - mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse); - } break; - - default: - LOG_WARNING(Service_Audio, - "ADSP AudioRenderer received an invalid message, msg={:02X}!", - static_cast<u32>(message)); - break; - } - } -} - -} // namespace AudioCore::AudioRenderer::ADSP diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h deleted file mode 100644 index 88e558183..000000000 --- a/src/audio_core/renderer/adsp/audio_renderer.h +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <memory> -#include <thread> - -#include "audio_core/renderer/adsp/command_buffer.h" -#include "audio_core/renderer/adsp/command_list_processor.h" -#include "common/common_types.h" -#include "common/polyfill_thread.h" -#include "common/reader_writer_queue.h" -#include "common/thread.h" - -namespace Core { -namespace Timing { -struct EventType; -} -class System; -} // namespace Core - -namespace AudioCore { -namespace Sink { -class Sink; -} - -namespace AudioRenderer::ADSP { - -enum class RenderMessage { - /* 0x00 */ Invalid, - /* 0x01 */ AudioRenderer_MapUnmap_Map, - /* 0x02 */ AudioRenderer_MapUnmap_MapResponse, - /* 0x03 */ AudioRenderer_MapUnmap_Unmap, - /* 0x04 */ AudioRenderer_MapUnmap_UnmapResponse, - /* 0x05 */ AudioRenderer_MapUnmap_InvalidateCache, - /* 0x06 */ AudioRenderer_MapUnmap_InvalidateCacheResponse, - /* 0x07 */ AudioRenderer_MapUnmap_Shutdown, - /* 0x08 */ AudioRenderer_MapUnmap_ShutdownResponse, - /* 0x16 */ AudioRenderer_InitializeOK = 0x16, - /* 0x20 */ AudioRenderer_RenderResponse = 0x20, - /* 0x2A */ AudioRenderer_Render = 0x2A, - /* 0x34 */ AudioRenderer_Shutdown = 0x34, -}; - -/** - * A mailbox for the AudioRenderer, allowing communication between the host and the AudioRenderer - * running on the ADSP. - */ -class AudioRenderer_Mailbox { -public: - /** - * Send a message from the host to the AudioRenderer. - * - * @param message - The message to send to the AudioRenderer. - */ - void HostSendMessage(RenderMessage message); - - /** - * Host wait for a message from the AudioRenderer. - * - * @return The message returned from the AudioRenderer. - */ - RenderMessage HostWaitMessage(); - - /** - * Send a message from the AudioRenderer to the host. - * - * @param message - The message to send to the host. - */ - void ADSPSendMessage(RenderMessage message); - - /** - * AudioRenderer wait for a message from the host. - * - * @return The message returned from the AudioRenderer. - */ - RenderMessage ADSPWaitMessage(); - - /** - * Get the command buffer with the given session id (0 or 1). - * - * @param session_id - The session id to get (0 or 1). - * @return The command buffer. - */ - CommandBuffer& GetCommandBuffer(u32 session_id); - - /** - * Set the command buffer with the given session id (0 or 1). - * - * @param session_id - The session id to get (0 or 1). - * @param buffer - The command buffer to set. - */ - void SetCommandBuffer(u32 session_id, const CommandBuffer& buffer); - - /** - * Get the total render time taken for the last command lists sent. - * - * @return Total render time taken for the last command lists. - */ - u64 GetRenderTimeTaken() const; - - /** - * Get the tick the AudioRenderer was signalled. - * - * @return The tick the AudioRenderer was signalled. - */ - u64 GetSignalledTick() const; - - /** - * Set the tick the AudioRenderer was signalled. - * - * @param tick - The tick the AudioRenderer was signalled. - */ - void SetSignalledTick(u64 tick); - - /** - * Clear the remaining command count. - * - * @param session_id - Index for which command list to clear (0 or 1). - */ - void ClearRemainCount(u32 session_id); - - /** - * Get the remaining command count for a given command list. - * - * @param session_id - Index for which command list to clear (0 or 1). - * @return The remaining command count. - */ - u32 GetRemainCommandCount(u32 session_id) const; - - /** - * Clear the command buffers (does not clear the time taken or the remaining command count). - */ - void ClearCommandBuffers(); - -private: - /// Host signalling event - Common::Event host_event{}; - /// AudioRenderer signalling event - Common::Event adsp_event{}; - /// Host message queue - - Common::ReaderWriterQueue<RenderMessage> host_messages{}; - /// AudioRenderer message queue - - Common::ReaderWriterQueue<RenderMessage> adsp_messages{}; - /// Command buffers - - std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; - /// Tick the AudioRnederer was signalled - u64 signalled_tick{}; -}; - -/** - * The AudioRenderer application running on the ADSP. - */ -class AudioRenderer { -public: - explicit AudioRenderer(Core::System& system); - ~AudioRenderer(); - - /** - * Start the AudioRenderer. - * - * @param mailbox The mailbox to use for this session. - */ - void Start(AudioRenderer_Mailbox* mailbox); - - /** - * Stop the AudioRenderer. - */ - void Stop(); - -private: - /** - * Main AudioRenderer thread, responsible for processing the command lists. - */ - void ThreadFunc(std::stop_token stop_token); - - /** - * Creates the streams which will receive the processed samples. - */ - void CreateSinkStreams(); - - /// Core system - Core::System& system; - /// Main thread - std::jthread thread{}; - /// The current state - std::atomic<bool> running{}; - /// The active mailbox - AudioRenderer_Mailbox* mailbox{}; - /// The command lists to process - std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; - /// The output sink the AudioRenderer will use - Sink::Sink& sink; - /// The streams which will receive the processed samples - std::array<Sink::SinkStream*, MaxRendererSessions> streams; -}; - -} // namespace AudioRenderer::ADSP -} // namespace AudioCore diff --git a/src/audio_core/renderer/adsp/command_buffer.h b/src/audio_core/renderer/adsp/command_buffer.h deleted file mode 100644 index 880b279d8..000000000 --- a/src/audio_core/renderer/adsp/command_buffer.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "audio_core/common/common.h" -#include "common/common_types.h" - -namespace AudioCore::AudioRenderer::ADSP { - -struct CommandBuffer { - CpuAddr buffer; - u64 size; - u64 time_limit; - u32 remaining_command_count; - bool reset_buffers; - u64 applet_resource_user_id; - u64 render_time_taken; -}; - -} // namespace AudioCore::AudioRenderer::ADSP diff --git a/src/audio_core/renderer/audio_device.cpp b/src/audio_core/renderer/audio_device.cpp index 0d9d8f6ce..2d9bf82bb 100644 --- a/src/audio_core/renderer/audio_device.cpp +++ b/src/audio_core/renderer/audio_device.cpp @@ -10,7 +10,7 @@ #include "audio_core/sink/sink.h" #include "core/core.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { constexpr std::array usb_device_names{ AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, @@ -71,4 +71,4 @@ f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const { return output_sink.GetDeviceVolume(); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/audio_device.h b/src/audio_core/renderer/audio_device.h index dd6be70ee..ca4040add 100644 --- a/src/audio_core/renderer/audio_device.h +++ b/src/audio_core/renderer/audio_device.h @@ -16,7 +16,7 @@ namespace Sink { class Sink; } -namespace AudioRenderer { +namespace Renderer { /** * An interface to an output audio device available to the Switch. */ @@ -76,5 +76,5 @@ private: const u32 user_revision; }; -} // namespace AudioRenderer +} // namespace Renderer } // namespace AudioCore diff --git a/src/audio_core/renderer/audio_renderer.cpp b/src/audio_core/renderer/audio_renderer.cpp index a8257eb2e..09efe9be9 100644 --- a/src/audio_core/renderer/audio_renderer.cpp +++ b/src/audio_core/renderer/audio_renderer.cpp @@ -9,7 +9,7 @@ #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/service/audio/errors.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) : core{system_}, manager{manager_}, system{system_, rendered_event} {} @@ -64,4 +64,4 @@ Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performa return system.Update(input, performance, output); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/audio_renderer.h b/src/audio_core/renderer/audio_renderer.h index 90c6f9727..24650278b 100644 --- a/src/audio_core/renderer/audio_renderer.h +++ b/src/audio_core/renderer/audio_renderer.h @@ -19,7 +19,7 @@ class KTransferMemory; namespace AudioCore { struct AudioRendererParameterInternal; -namespace AudioRenderer { +namespace Renderer { class Manager; /** @@ -31,7 +31,7 @@ public: /** * Initialize the renderer. - * Registers the system with the AudioRenderer::Manager, allocates workbuffers and initializes + * Registers the system with the Renderer::Manager, allocates workbuffers and initializes * everything to a default state. * * @param params - Input parameters to initialize the system with. @@ -93,5 +93,5 @@ private: System system; }; -} // namespace AudioRenderer +} // namespace Renderer } // namespace AudioCore diff --git a/src/audio_core/renderer/behavior/behavior_info.cpp b/src/audio_core/renderer/behavior/behavior_info.cpp index 3d2a91312..058539042 100644 --- a/src/audio_core/renderer/behavior/behavior_info.cpp +++ b/src/audio_core/renderer/behavior/behavior_info.cpp @@ -4,7 +4,7 @@ #include "audio_core/common/feature_support.h" #include "audio_core/renderer/behavior/behavior_info.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { BehaviorInfo::BehaviorInfo() : process_revision{CurrentRevision} {} @@ -190,4 +190,4 @@ bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const { return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/behavior/behavior_info.h b/src/audio_core/renderer/behavior/behavior_info.h index b52340229..a4958857a 100644 --- a/src/audio_core/renderer/behavior/behavior_info.h +++ b/src/audio_core/renderer/behavior/behavior_info.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "core/hle/service/audio/errors.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Holds host and user revisions, checks whether render features can be enabled, and reports errors. */ @@ -264,7 +264,7 @@ public: /** * Check if skipping voice pitch and sample rate conversion is supported. * This speeds up the data source commands by skipping resampling if unwanted. - * See AudioCore::AudioRenderer::DecodeFromWaveBuffers + * See AudioCore::Renderer::DecodeFromWaveBuffers * * @return True if supported, otherwise false. */ @@ -273,7 +273,7 @@ public: /** * Check if resetting played sample count at loop points is supported. * This resets the number of samples played in a voice state when a loop point is reached. - * See AudioCore::AudioRenderer::DecodeFromWaveBuffers + * See AudioCore::Renderer::DecodeFromWaveBuffers * * @return True if supported, otherwise false. */ @@ -373,4 +373,4 @@ public: u32 error_count{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp index e312eb166..667711e17 100644 --- a/src/audio_core/renderer/behavior/info_updater.cpp +++ b/src/audio_core/renderer/behavior/info_updater.cpp @@ -15,7 +15,7 @@ #include "audio_core/renderer/splitter/splitter_context.h" #include "audio_core/renderer/voice/voice_context.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_, const u32 process_handle_, BehaviorInfo& behaviour_) @@ -536,4 +536,4 @@ Result InfoUpdater::CheckConsumedSize() { return ResultSuccess; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/behavior/info_updater.h b/src/audio_core/renderer/behavior/info_updater.h index c817d8d8d..fb4b7d25a 100644 --- a/src/audio_core/renderer/behavior/info_updater.h +++ b/src/audio_core/renderer/behavior/info_updater.h @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "core/hle/service/audio/errors.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class BehaviorInfo; class VoiceContext; class MixContext; @@ -202,4 +202,4 @@ private: BehaviorInfo& behaviour; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp index 0bd418306..67d43e69a 100644 --- a/src/audio_core/renderer/command/command_buffer.cpp +++ b/src/audio_core/renderer/command/command_buffer.cpp @@ -16,7 +16,7 @@ #include "audio_core/renderer/voice/voice_info.h" #include "audio_core/renderer/voice/voice_state.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { template <typename T, CommandId Id> T& CommandBuffer::GenerateStart(const s32 node_id) { @@ -713,4 +713,4 @@ void CommandBuffer::GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& GenerateEnd<CompressorCommand>(cmd); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/command_buffer.h b/src/audio_core/renderer/command/command_buffer.h index 162170846..12e8c2c81 100644 --- a/src/audio_core/renderer/command/command_buffer.h +++ b/src/audio_core/renderer/command/command_buffer.h @@ -10,7 +10,7 @@ #include "audio_core/renderer/performance/performance_manager.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { struct UpsamplerInfo; struct VoiceState; class EffectInfoBase; @@ -465,4 +465,4 @@ private: void GenerateEnd(T& cmd); }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp index fba84c7bd..ccb186209 100644 --- a/src/audio_core/renderer/command/command_generator.cpp +++ b/src/audio_core/renderer/command/command_generator.cpp @@ -21,7 +21,7 @@ #include "audio_core/renderer/voice/voice_context.h" #include "common/alignment.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { CommandGenerator::CommandGenerator(CommandBuffer& command_buffer_, const CommandListHeader& command_list_header_, @@ -793,4 +793,4 @@ void CommandGenerator::GeneratePerformanceCommand( command_buffer.GeneratePerformanceCommand(node_id, state, entry_addresses); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/command_generator.h b/src/audio_core/renderer/command/command_generator.h index b3cd7b408..38ee2a64e 100644 --- a/src/audio_core/renderer/command/command_generator.h +++ b/src/audio_core/renderer/command/command_generator.h @@ -12,7 +12,7 @@ namespace AudioCore { struct AudioRendererSystemContext; -namespace AudioRenderer { +namespace Renderer { class CommandBuffer; struct CommandListHeader; class VoiceContext; @@ -345,5 +345,5 @@ private: PerformanceManager* performance_manager; }; -} // namespace AudioRenderer +} // namespace Renderer } // namespace AudioCore diff --git a/src/audio_core/renderer/command/command_list_header.h b/src/audio_core/renderer/command/command_list_header.h index 988530b1f..de9ee070b 100644 --- a/src/audio_core/renderer/command/command_list_header.h +++ b/src/audio_core/renderer/command/command_list_header.h @@ -8,7 +8,7 @@ #include "audio_core/common/common.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { struct CommandListHeader { u64 buffer_size; @@ -19,4 +19,4 @@ struct CommandListHeader { u32 sample_rate; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.cpp b/src/audio_core/renderer/command/command_processing_time_estimator.cpp index 3091f587a..a48a016b1 100644 --- a/src/audio_core/renderer/command/command_processing_time_estimator.cpp +++ b/src/audio_core/renderer/command/command_processing_time_estimator.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/command/command_processing_time_estimator.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { u32 CommandProcessingTimeEstimatorVersion1::Estimate( const PcmInt16DataSourceVersion1Command& command) const { @@ -3617,4 +3617,4 @@ u32 CommandProcessingTimeEstimatorVersion5::Estimate(const CompressorCommand& co } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.h b/src/audio_core/renderer/command/command_processing_time_estimator.h index 452217196..1c76e4ba4 100644 --- a/src/audio_core/renderer/command/command_processing_time_estimator.h +++ b/src/audio_core/renderer/command/command_processing_time_estimator.h @@ -6,7 +6,7 @@ #include "audio_core/renderer/command/commands.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Estimate the processing time required for all commands. */ @@ -251,4 +251,4 @@ private: u32 buffer_count{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/data_source/adpcm.cpp b/src/audio_core/renderer/command/data_source/adpcm.cpp index e66ed2990..e7f82d3b3 100644 --- a/src/audio_core/renderer/command/data_source/adpcm.cpp +++ b/src/audio_core/renderer/command/data_source/adpcm.cpp @@ -3,23 +3,29 @@ #include <span> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/data_source/adpcm.h" #include "audio_core/renderer/command/data_source/decode.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void AdpcmDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, +void AdpcmDataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("AdpcmDataSourceVersion1Command\n\toutput_index {:02X} source sample " "rate {} target sample rate {} src quality {}\n", output_index, sample_rate, processor.target_sample_rate, src_quality); } -void AdpcmDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { +void AdpcmDataSourceVersion1Command::Process(const AudioRenderer::CommandListProcessor& processor) { 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}, @@ -41,18 +47,18 @@ void AdpcmDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& p DecodeFromWaveBuffers(*processor.memory, args); } -bool AdpcmDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { +bool AdpcmDataSourceVersion1Command::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -void AdpcmDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, +void AdpcmDataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("AdpcmDataSourceVersion2Command\n\toutput_index {:02X} source sample " "rate {} target sample rate {} src quality {}\n", output_index, sample_rate, processor.target_sample_rate, src_quality); } -void AdpcmDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { +void AdpcmDataSourceVersion2Command::Process(const AudioRenderer::CommandListProcessor& processor) { auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count)}; @@ -77,8 +83,8 @@ void AdpcmDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& p DecodeFromWaveBuffers(*processor.memory, args); } -bool AdpcmDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { +bool AdpcmDataSourceVersion2Command::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/data_source/adpcm.h b/src/audio_core/renderer/command/data_source/adpcm.h index a9cf9cee4..487846f0c 100644 --- a/src/audio_core/renderer/command/data_source/adpcm.h +++ b/src/audio_core/renderer/command/data_source/adpcm.h @@ -11,11 +11,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command to decode ADPCM-encoded version 1 wavebuffers * into the output_index mix buffer. @@ -27,14 +28,14 @@ struct AdpcmDataSourceVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -42,13 +43,13 @@ struct AdpcmDataSourceVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Quality used for sample rate conversion SrcQuality src_quality; /// Mix buffer index for decoded samples s16 output_index; - /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) + /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) u16 flags; /// Wavebuffer sample rate u32 sample_rate; @@ -75,14 +76,14 @@ struct AdpcmDataSourceVersion2Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -90,13 +91,13 @@ struct AdpcmDataSourceVersion2Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Quality used for sample rate conversion SrcQuality src_quality; /// Mix buffer index for decoded samples s16 output_index; - /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) + /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) u16 flags; /// Wavebuffer sample rate u32 sample_rate; @@ -116,4 +117,4 @@ struct AdpcmDataSourceVersion2Command : ICommand { u64 data_size; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp index 257aa866e..911dae3c1 100644 --- a/src/audio_core/renderer/command/data_source/decode.cpp +++ b/src/audio_core/renderer/command/data_source/decode.cpp @@ -11,7 +11,7 @@ #include "common/scratch_buffer.h" #include "core/memory.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { constexpr u32 TempBufferSize = 0x3F00; constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4}; @@ -123,11 +123,13 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> 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<s16> 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 (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; - } + if (samples_decoded && offset < end_offset - start_offset) { + continue; + } + + 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); } } @@ -423,4 +425,4 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf voice_state.fraction = fraction; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/data_source/decode.h b/src/audio_core/renderer/command/data_source/decode.h index 4d63d6fa8..5f52f32f0 100644 --- a/src/audio_core/renderer/command/data_source/decode.h +++ b/src/audio_core/renderer/command/data_source/decode.h @@ -15,7 +15,7 @@ namespace Core::Memory { class Memory; } -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { struct DecodeFromWaveBuffersArgs { SampleFormat sample_format; @@ -56,4 +56,4 @@ struct DecodeArg { */ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer 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 be77fab69..d1f685656 100644 --- a/src/audio_core/renderer/command/data_source/pcm_float.cpp +++ b/src/audio_core/renderer/command/data_source/pcm_float.cpp @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/data_source/decode.h" #include "audio_core/renderer/command/data_source/pcm_float.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void PcmFloatDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, +void PcmFloatDataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("PcmFloatDataSourceVersion1Command\n\toutput_index {:02X} channel {} " @@ -16,10 +16,17 @@ void PcmFloatDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& p processor.target_sample_rate, src_quality); } -void PcmFloatDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { +void PcmFloatDataSourceVersion1Command::Process( + const AudioRenderer::CommandListProcessor& processor) { 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}, @@ -41,11 +48,12 @@ void PcmFloatDataSourceVersion1Command::Process(const ADSP::CommandListProcessor DecodeFromWaveBuffers(*processor.memory, args); } -bool PcmFloatDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { +bool PcmFloatDataSourceVersion1Command::Verify( + const AudioRenderer::CommandListProcessor& processor) { return true; } -void PcmFloatDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, +void PcmFloatDataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("PcmFloatDataSourceVersion2Command\n\toutput_index {:02X} channel {} " @@ -54,7 +62,8 @@ void PcmFloatDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& p processor.target_sample_rate, src_quality); } -void PcmFloatDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { +void PcmFloatDataSourceVersion2Command::Process( + const AudioRenderer::CommandListProcessor& processor) { auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count); @@ -79,8 +88,9 @@ void PcmFloatDataSourceVersion2Command::Process(const ADSP::CommandListProcessor DecodeFromWaveBuffers(*processor.memory, args); } -bool PcmFloatDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { +bool PcmFloatDataSourceVersion2Command::Verify( + const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/data_source/pcm_float.h b/src/audio_core/renderer/command/data_source/pcm_float.h index e4af77c20..2c9d1877e 100644 --- a/src/audio_core/renderer/command/data_source/pcm_float.h +++ b/src/audio_core/renderer/command/data_source/pcm_float.h @@ -9,11 +9,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command to decode PCM float-encoded version 1 wavebuffers * into the output_index mix buffer. @@ -25,14 +26,14 @@ struct PcmFloatDataSourceVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,13 +41,13 @@ struct PcmFloatDataSourceVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Quality used for sample rate conversion SrcQuality src_quality; /// Mix buffer index for decoded samples s16 output_index; - /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) + /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) u16 flags; /// Wavebuffer sample rate u32 sample_rate; @@ -73,14 +74,14 @@ struct PcmFloatDataSourceVersion2Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -88,13 +89,13 @@ struct PcmFloatDataSourceVersion2Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Quality used for sample rate conversion SrcQuality src_quality; /// Mix buffer index for decoded samples s16 output_index; - /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) + /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) u16 flags; /// Wavebuffer sample rate u32 sample_rate; @@ -110,4 +111,4 @@ struct PcmFloatDataSourceVersion2Command : ICommand { CpuAddr voice_state; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer 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 7a27463e4..c89a5aaac 100644 --- a/src/audio_core/renderer/command/data_source/pcm_int16.cpp +++ b/src/audio_core/renderer/command/data_source/pcm_int16.cpp @@ -3,13 +3,13 @@ #include <span> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/data_source/decode.h" #include "audio_core/renderer/command/data_source/pcm_int16.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void PcmInt16DataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, +void PcmInt16DataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("PcmInt16DataSourceVersion1Command\n\toutput_index {:02X} channel {} " @@ -18,10 +18,17 @@ void PcmInt16DataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& p processor.target_sample_rate, src_quality); } -void PcmInt16DataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { +void PcmInt16DataSourceVersion1Command::Process( + const AudioRenderer::CommandListProcessor& processor) { 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}, @@ -43,11 +50,12 @@ void PcmInt16DataSourceVersion1Command::Process(const ADSP::CommandListProcessor DecodeFromWaveBuffers(*processor.memory, args); } -bool PcmInt16DataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { +bool PcmInt16DataSourceVersion1Command::Verify( + const AudioRenderer::CommandListProcessor& processor) { return true; } -void PcmInt16DataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, +void PcmInt16DataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("PcmInt16DataSourceVersion2Command\n\toutput_index {:02X} channel {} " @@ -56,7 +64,8 @@ void PcmInt16DataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& p processor.target_sample_rate, src_quality); } -void PcmInt16DataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { +void PcmInt16DataSourceVersion2Command::Process( + const AudioRenderer::CommandListProcessor& processor) { auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count); DecodeFromWaveBuffersArgs args{ @@ -80,8 +89,9 @@ void PcmInt16DataSourceVersion2Command::Process(const ADSP::CommandListProcessor DecodeFromWaveBuffers(*processor.memory, args); } -bool PcmInt16DataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { +bool PcmInt16DataSourceVersion2Command::Verify( + const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.h b/src/audio_core/renderer/command/data_source/pcm_int16.h index 5de1ad60d..2c013f003 100644 --- a/src/audio_core/renderer/command/data_source/pcm_int16.h +++ b/src/audio_core/renderer/command/data_source/pcm_int16.h @@ -9,11 +9,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command to decode PCM s16-encoded version 1 wavebuffers * into the output_index mix buffer. @@ -25,14 +26,14 @@ struct PcmInt16DataSourceVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,13 +41,13 @@ struct PcmInt16DataSourceVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Quality used for sample rate conversion SrcQuality src_quality; /// Mix buffer index for decoded samples s16 output_index; - /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) + /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) u16 flags; /// Wavebuffer sample rate u32 sample_rate; @@ -72,26 +73,26 @@ struct PcmInt16DataSourceVersion2Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Quality used for sample rate conversion SrcQuality src_quality; /// Mix buffer index for decoded samples s16 output_index; - /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) + /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags) u16 flags; /// Wavebuffer sample rate u32 sample_rate; @@ -107,4 +108,4 @@ struct PcmInt16DataSourceVersion2Command : ICommand { CpuAddr voice_state; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/aux_.cpp b/src/audio_core/renderer/command/effect/aux_.cpp index a3e12b3e7..74d9c229f 100644 --- a/src/audio_core/renderer/command/effect/aux_.cpp +++ b/src/audio_core/renderer/command/effect/aux_.cpp @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/aux_.h" #include "audio_core/renderer/effect/aux_.h" #include "core/core.h" #include "core/memory.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Reset an AuxBuffer. * @@ -175,13 +175,13 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_, return read_count_; } -void AuxCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void AuxCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("AuxCommand\n\tenabled {} input {:02X} output {:02X}\n", effect_enabled, input, output); } -void AuxCommand::Process(const ADSP::CommandListProcessor& processor) { +void AuxCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto input_buffer{ processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; auto output_buffer{ @@ -208,8 +208,8 @@ void AuxCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool AuxCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool AuxCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/aux_.h b/src/audio_core/renderer/command/effect/aux_.h index 825c93732..da1e55261 100644 --- a/src/audio_core/renderer/command/effect/aux_.h +++ b/src/audio_core/renderer/command/effect/aux_.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command to read and write an auxiliary buffer, writing the input mix buffer to game * memory, and reading into the output buffer from game memory. @@ -24,14 +25,14 @@ struct AuxCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -39,7 +40,7 @@ struct AuxCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer index s16 input; @@ -63,4 +64,4 @@ struct AuxCommand : ICommand { bool effect_enabled; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp index dea6423dc..3392e7747 100644 --- a/src/audio_core/renderer/command/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/biquad_filter.h" #include "audio_core/renderer/voice/voice_state.h" #include "common/bit_cast.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Biquad filter float implementation. * @@ -76,14 +76,14 @@ static void ApplyBiquadFilterInt(std::span<s32> output, std::span<const s32> inp } } -void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void BiquadFilterCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format( "BiquadFilterCommand\n\tinput {:02X} output {:02X} needs_init {} use_float_processing {}\n", input, output, needs_init, use_float_processing); } -void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { +void BiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; if (needs_init) { *state_ = {}; @@ -103,8 +103,8 @@ void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool BiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool BiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/biquad_filter.h b/src/audio_core/renderer/command/effect/biquad_filter.h index 4c9c42d29..0e903930a 100644 --- a/src/audio_core/renderer/command/effect/biquad_filter.h +++ b/src/audio_core/renderer/command/effect/biquad_filter.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/voice/voice_state.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for applying a biquad filter to the input mix buffer, saving the results to * the output mix buffer. @@ -26,14 +27,14 @@ struct BiquadFilterCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -41,7 +42,7 @@ struct BiquadFilterCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer index s16 input; @@ -71,4 +72,4 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input, std::array<s16, 3>& b, std::array<s16, 2>& a, VoiceState::BiquadFilterState& state, const u32 sample_count); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/capture.cpp b/src/audio_core/renderer/command/effect/capture.cpp index 042fd286e..f235ce027 100644 --- a/src/audio_core/renderer/command/effect/capture.cpp +++ b/src/audio_core/renderer/command/effect/capture.cpp @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/capture.h" #include "audio_core/renderer/effect/aux_.h" #include "core/memory.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Reset an AuxBuffer. * @@ -118,13 +118,13 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_in return write_count_; } -void CaptureCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void CaptureCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("CaptureCommand\n\tenabled {} input {:02X} output {:02X}", effect_enabled, input, output); } -void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) { +void CaptureCommand::Process(const AudioRenderer::CommandListProcessor& processor) { if (effect_enabled) { auto input_buffer{ processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; @@ -135,8 +135,8 @@ void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool CaptureCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool CaptureCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/capture.h b/src/audio_core/renderer/command/effect/capture.h index 8670acb24..a0016c6f6 100644 --- a/src/audio_core/renderer/command/effect/capture.h +++ b/src/audio_core/renderer/command/effect/capture.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for capturing a mix buffer. That is, writing it back to a given game memory * address. @@ -24,14 +25,14 @@ struct CaptureCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -39,7 +40,7 @@ struct CaptureCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer index s16 input; @@ -59,4 +60,4 @@ struct CaptureCommand : ICommand { bool effect_enabled; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp index ee9b68d5b..7ff707f4e 100644 --- a/src/audio_core/renderer/command/effect/compressor.cpp +++ b/src/audio_core/renderer/command/effect/compressor.cpp @@ -5,11 +5,11 @@ #include <span> #include <vector> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/compressor.h" #include "audio_core/renderer/effect/compressor.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { static void SetCompressorEffectParameter(const CompressorInfo::ParameterVersion2& params, CompressorInfo::State& state) { @@ -110,7 +110,7 @@ static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& param } } -void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void CompressorCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled); for (s16 i = 0; i < parameter.channel_count; i++) { @@ -123,7 +123,7 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& string += "\n"; } -void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { +void CompressorCommand::Process(const AudioRenderer::CommandListProcessor& processor) { std::array<std::span<const s32>, MaxChannels> input_buffers{}; std::array<std::span<s32>, MaxChannels> output_buffers{}; @@ -148,8 +148,8 @@ void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { processor.sample_count); } -bool CompressorCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool CompressorCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/compressor.h b/src/audio_core/renderer/command/effect/compressor.h index f8e96cb43..c011aa927 100644 --- a/src/audio_core/renderer/command/effect/compressor.h +++ b/src/audio_core/renderer/command/effect/compressor.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/effect/compressor.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for limiting volume between a high and low threshold. * Version 1. @@ -26,14 +27,14 @@ struct CompressorCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -41,7 +42,7 @@ struct CompressorCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer offsets for each channel std::array<s16, MaxChannels> inputs; @@ -57,4 +58,4 @@ struct CompressorCommand : ICommand { bool effect_enabled; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp index e536cbb1e..ffb298c07 100644 --- a/src/audio_core/renderer/command/effect/delay.cpp +++ b/src/audio_core/renderer/command/effect/delay.cpp @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/delay.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Update the DelayInfo state according to the given parameters. * @@ -194,7 +194,7 @@ static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayIn } } -void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void DelayCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("DelayCommand\n\tenabled {} \n\tinputs: ", effect_enabled); for (u32 i = 0; i < MaxChannels; i++) { @@ -207,7 +207,7 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce string += "\n"; } -void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { +void DelayCommand::Process(const AudioRenderer::CommandListProcessor& processor) { std::array<std::span<const s32>, MaxChannels> input_buffers{}; std::array<std::span<s32>, MaxChannels> output_buffers{}; @@ -231,8 +231,8 @@ void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { processor.sample_count); } -bool DelayCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool DelayCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/delay.h b/src/audio_core/renderer/command/effect/delay.h index b7a15ae6b..bfeac7af4 100644 --- a/src/audio_core/renderer/command/effect/delay.h +++ b/src/audio_core/renderer/command/effect/delay.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/effect/delay.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for a delay effect. Delays inputs mix buffers according to the parameters * and state, outputs receives the delayed samples. @@ -26,14 +27,14 @@ struct DelayCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -41,7 +42,7 @@ struct DelayCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer offsets for each channel std::array<s16, MaxChannels> inputs; @@ -57,4 +58,4 @@ struct DelayCommand : ICommand { bool effect_enabled; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp index d2bfb67cc..ecfdfabc6 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp @@ -3,11 +3,11 @@ #include <numbers> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/i3dl2_reverb.h" #include "common/polyfill_ranges.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> MinDelayLineTimes{ 5.0f, @@ -394,7 +394,7 @@ static void ApplyI3dl2ReverbEffect(const I3dl2ReverbInfo::ParameterVersion1& par } } -void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void I3dl2ReverbCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("I3dl2ReverbCommand\n\tenabled {} \n\tinputs: ", effect_enabled); for (u32 i = 0; i < parameter.channel_count; i++) { @@ -407,7 +407,7 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& string += "\n"; } -void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { +void I3dl2ReverbCommand::Process(const AudioRenderer::CommandListProcessor& processor) { std::array<std::span<const s32>, MaxChannels> input_buffers{}; std::array<std::span<s32>, MaxChannels> output_buffers{}; @@ -431,8 +431,8 @@ void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { processor.sample_count); } -bool I3dl2ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool I3dl2ReverbCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.h b/src/audio_core/renderer/command/effect/i3dl2_reverb.h index 243877056..e4c538ae8 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.h +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/effect/i3dl2.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for a I3DL2Reverb effect. Apply a reverb to inputs mix buffer according to * the I3DL2 spec, outputs receives the results. @@ -26,14 +27,14 @@ struct I3dl2ReverbCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -41,7 +42,7 @@ struct I3dl2ReverbCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer offsets for each channel std::array<s16, MaxChannels> inputs; @@ -57,4 +58,4 @@ struct I3dl2ReverbCommand : ICommand { bool effect_enabled; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp index 4161a9821..63aa06f5c 100644 --- a/src/audio_core/renderer/command/effect/light_limiter.cpp +++ b/src/audio_core/renderer/command/effect/light_limiter.cpp @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/light_limiter.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Update the LightLimiterInfo state according to the given parameters. * A no-op. @@ -133,8 +133,8 @@ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& p } } -void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void LightLimiterVersion1Command::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("LightLimiterVersion1Command\n\tinputs: "); for (u32 i = 0; i < MaxChannels; i++) { string += fmt::format("{:02X}, ", inputs[i]); @@ -146,7 +146,7 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP string += "\n"; } -void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) { +void LightLimiterVersion1Command::Process(const AudioRenderer::CommandListProcessor& processor) { std::array<std::span<const s32>, MaxChannels> input_buffers{}; std::array<std::span<s32>, MaxChannels> output_buffers{}; @@ -172,12 +172,12 @@ void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& proc processor.sample_count, statistics); } -bool LightLimiterVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { +bool LightLimiterVersion1Command::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void LightLimiterVersion2Command::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("LightLimiterVersion2Command\n\tinputs: \n"); for (u32 i = 0; i < MaxChannels; i++) { string += fmt::format("{:02X}, ", inputs[i]); @@ -189,7 +189,7 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP string += "\n"; } -void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { +void LightLimiterVersion2Command::Process(const AudioRenderer::CommandListProcessor& processor) { std::array<std::span<const s32>, MaxChannels> input_buffers{}; std::array<std::span<s32>, MaxChannels> output_buffers{}; @@ -215,8 +215,8 @@ void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& proc processor.sample_count, statistics); } -bool LightLimiterVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { +bool LightLimiterVersion2Command::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/light_limiter.h b/src/audio_core/renderer/command/effect/light_limiter.h index 5d98272c7..6e3ee1b53 100644 --- a/src/audio_core/renderer/command/effect/light_limiter.h +++ b/src/audio_core/renderer/command/effect/light_limiter.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/effect/light_limiter.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for limiting volume between a high and low threshold. * Version 1. @@ -26,14 +27,14 @@ struct LightLimiterVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -41,7 +42,7 @@ struct LightLimiterVersion1Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer offsets for each channel std::array<s16, MaxChannels> inputs; @@ -68,21 +69,21 @@ struct LightLimiterVersion2Command : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. * * @param processor - The CommandListProcessor processing this command. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer offsets for each channel std::array<s16, MaxChannels> inputs; @@ -100,4 +101,4 @@ struct LightLimiterVersion2Command : ICommand { bool effect_enabled; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp index 48a7cba8a..208bbeaf2 100644 --- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp @@ -1,20 +1,20 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/biquad_filter.h" #include "audio_core/renderer/command/effect/multi_tap_biquad_filter.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void MultiTapBiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void MultiTapBiquadFilterCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format( "MultiTapBiquadFilterCommand\n\tinput {:02X}\n\toutput {:02X}\n\tneeds_init ({}, {})\n", input, output, needs_init[0], needs_init[1]); } -void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { +void MultiTapBiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& processor) { if (filter_tap_count > MaxBiquadFilters) { LOG_ERROR(Service_Audio, "Too many filter taps! {}", filter_tap_count); filter_tap_count = MaxBiquadFilters; @@ -38,8 +38,8 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc } } -bool MultiTapBiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool MultiTapBiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h index 99c2c0830..50fce80b0 100644 --- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h +++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/voice/voice_info.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for applying multiple biquads at once. */ @@ -25,14 +26,14 @@ struct MultiTapBiquadFilterCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,7 +41,7 @@ struct MultiTapBiquadFilterCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer index s16 input; @@ -56,4 +57,4 @@ struct MultiTapBiquadFilterCommand : ICommand { u8 filter_tap_count; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp index fc2f15a5e..7f152a962 100644 --- a/src/audio_core/renderer/command/effect/reverb.cpp +++ b/src/audio_core/renderer/command/effect/reverb.cpp @@ -4,11 +4,11 @@ #include <numbers> #include <ranges> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/effect/reverb.h" #include "common/polyfill_ranges.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { constexpr std::array<f32, ReverbInfo::MaxDelayLines> FdnMaxDelayLineTimes = { 53.9532470703125f, @@ -396,7 +396,7 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever } } -void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void ReverbCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format( "ReverbCommand\n\tenabled {} long_size_pre_delay_supported {}\n\tinputs: ", effect_enabled, @@ -411,7 +411,7 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc string += "\n"; } -void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { +void ReverbCommand::Process(const AudioRenderer::CommandListProcessor& processor) { std::array<std::span<const s32>, MaxChannels> input_buffers{}; std::array<std::span<s32>, MaxChannels> output_buffers{}; @@ -435,8 +435,8 @@ void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { processor.sample_count); } -bool ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool ReverbCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/effect/reverb.h b/src/audio_core/renderer/command/effect/reverb.h index 328756150..2056c73f2 100644 --- a/src/audio_core/renderer/command/effect/reverb.h +++ b/src/audio_core/renderer/command/effect/reverb.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/effect/reverb.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for a Reverb effect. Apply a reverb to inputs mix buffer, outputs receives * the results. @@ -26,14 +27,14 @@ struct ReverbCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -41,7 +42,7 @@ struct ReverbCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer offsets for each channel std::array<s16, MaxChannels> inputs; @@ -59,4 +60,4 @@ struct ReverbCommand : ICommand { bool long_size_pre_delay_supported; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/icommand.h b/src/audio_core/renderer/command/icommand.h index f2dd41254..10a78ddf2 100644 --- a/src/audio_core/renderer/command/icommand.h +++ b/src/audio_core/renderer/command/icommand.h @@ -3,14 +3,18 @@ #pragma once +#include <string> + #include "audio_core/common/common.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { +using namespace ::AudioCore::ADSP; + enum class CommandId : u8 { /* 0x00 */ Invalid, /* 0x01 */ DataSourcePcmInt16Version1, @@ -59,14 +63,15 @@ struct ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - virtual void Dump(const ADSP::CommandListProcessor& processor, std::string& string) = 0; + virtual void Dump(const AudioRenderer::CommandListProcessor& processor, + std::string& string) = 0; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - virtual void Process(const ADSP::CommandListProcessor& processor) = 0; + virtual void Process(const AudioRenderer::CommandListProcessor& processor) = 0; /** * Verify this command's data is valid. @@ -74,7 +79,7 @@ struct ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - virtual bool Verify(const ADSP::CommandListProcessor& processor) = 0; + virtual bool Verify(const AudioRenderer::CommandListProcessor& processor) = 0; /// Command magic 0xCAFEBABE u32 magic{}; @@ -90,4 +95,4 @@ struct ICommand { u32 node_id{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/clear_mix.cpp b/src/audio_core/renderer/command/mix/clear_mix.cpp index 4f649d6a8..060d7cb28 100644 --- a/src/audio_core/renderer/command/mix/clear_mix.cpp +++ b/src/audio_core/renderer/command/mix/clear_mix.cpp @@ -3,22 +3,22 @@ #include <string> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/clear_mix.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void ClearMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void ClearMixBufferCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("ClearMixBufferCommand\n"); } -void ClearMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) { +void ClearMixBufferCommand::Process(const AudioRenderer::CommandListProcessor& processor) { memset(processor.mix_buffers.data(), 0, processor.mix_buffers.size_bytes()); } -bool ClearMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool ClearMixBufferCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/clear_mix.h b/src/audio_core/renderer/command/mix/clear_mix.h index 956ec0b65..650fa1a8a 100644 --- a/src/audio_core/renderer/command/mix/clear_mix.h +++ b/src/audio_core/renderer/command/mix/clear_mix.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for a clearing the mix buffers. * Used at the start of each command list. @@ -24,14 +25,14 @@ struct ClearMixBufferCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -39,7 +40,7 @@ struct ClearMixBufferCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/copy_mix.cpp b/src/audio_core/renderer/command/mix/copy_mix.cpp index 1d49f1644..5d386f95a 100644 --- a/src/audio_core/renderer/command/mix/copy_mix.cpp +++ b/src/audio_core/renderer/command/mix/copy_mix.cpp @@ -1,18 +1,18 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/copy_mix.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void CopyMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void CopyMixBufferCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("CopyMixBufferCommand\n\tinput {:02X} output {:02X}\n", input_index, output_index); } -void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) { +void CopyMixBufferCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count)}; auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, @@ -20,8 +20,8 @@ void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) std::memcpy(output.data(), input.data(), processor.sample_count * sizeof(s32)); } -bool CopyMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool CopyMixBufferCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/copy_mix.h b/src/audio_core/renderer/command/mix/copy_mix.h index a59007fb6..ae247c3f8 100644 --- a/src/audio_core/renderer/command/mix/copy_mix.h +++ b/src/audio_core/renderer/command/mix/copy_mix.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for a copying a mix buffer from input to output. */ @@ -23,14 +24,14 @@ struct CopyMixBufferCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -38,7 +39,7 @@ struct CopyMixBufferCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer index s16 input_index; @@ -46,4 +47,4 @@ struct CopyMixBufferCommand : ICommand { s16 output_index; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp index c2bc10061..caedb56b7 100644 --- a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp +++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/common/common.h" -#include "audio_core/renderer/adsp/command_list_processor.h" #include "audio_core/renderer/command/mix/depop_for_mix_buffers.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Apply depopping. Add the depopped sample to each incoming new sample, decaying it each time * according to decay. @@ -36,13 +36,13 @@ static s32 ApplyDepopMix(std::span<s32> output, const s32 depop_sample, } } -void DepopForMixBuffersCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void DepopForMixBuffersCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("DepopForMixBuffersCommand\n\tinput {:02X} count {} decay {}\n", input, count, decay.to_float()); } -void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& processor) { +void DepopForMixBuffersCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto end_index{std::min(processor.buffer_count, input + count)}; std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index}; @@ -57,8 +57,8 @@ void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& proces } } -bool DepopForMixBuffersCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool DepopForMixBuffersCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h index e7268ff27..699d38988 100644 --- a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h +++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h @@ -9,11 +9,12 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for depopping a mix buffer. * Adds a cumulation of previous samples to the current mix buffer with a decay. @@ -25,14 +26,14 @@ struct DepopForMixBuffersCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,7 +41,7 @@ struct DepopForMixBuffersCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Starting input mix buffer index u32 input; @@ -52,4 +53,4 @@ struct DepopForMixBuffersCommand : ICommand { CpuAddr depop_buffer; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/depop_prepare.cpp b/src/audio_core/renderer/command/mix/depop_prepare.cpp index 69bb78ccc..2faf4681a 100644 --- a/src/audio_core/renderer/command/mix/depop_prepare.cpp +++ b/src/audio_core/renderer/command/mix/depop_prepare.cpp @@ -1,15 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/depop_prepare.h" #include "audio_core/renderer/voice/voice_state.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void DepopPrepareCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("DepopPrepareCommand\n\tinputs: "); for (u32 i = 0; i < buffer_count; i++) { string += fmt::format("{:02X}, ", inputs[i]); @@ -17,7 +17,7 @@ void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor string += "\n"; } -void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) { +void DepopPrepareCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto samples{reinterpret_cast<s32*>(previous_samples)}; auto buffer{reinterpret_cast<s32*>(depop_buffer)}; @@ -29,8 +29,8 @@ void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool DepopPrepareCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool DepopPrepareCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/depop_prepare.h b/src/audio_core/renderer/command/mix/depop_prepare.h index a5465da9a..161a94461 100644 --- a/src/audio_core/renderer/command/mix/depop_prepare.h +++ b/src/audio_core/renderer/command/mix/depop_prepare.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for preparing depop. * Adds the previusly output last samples to the depop buffer. @@ -24,14 +25,14 @@ struct DepopPrepareCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -39,7 +40,7 @@ struct DepopPrepareCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Depop buffer offset for each mix buffer std::array<s16, MaxMixBuffers> inputs; @@ -51,4 +52,4 @@ struct DepopPrepareCommand : ICommand { CpuAddr depop_buffer; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/mix.cpp b/src/audio_core/renderer/command/mix/mix.cpp index 8ecf9b05a..8bd689b88 100644 --- a/src/audio_core/renderer/command/mix/mix.cpp +++ b/src/audio_core/renderer/command/mix/mix.cpp @@ -5,11 +5,11 @@ #include <limits> #include <span> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/mix.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Mix input mix buffer into output mix buffer, with volume applied to the input. * @@ -28,7 +28,7 @@ static void ApplyMix(std::span<s32> output, std::span<const s32> input, const f3 } } -void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void MixCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("MixCommand"); string += fmt::format("\n\tinput {:02X}", input_index); @@ -37,7 +37,7 @@ void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& process string += "\n"; } -void MixCommand::Process(const ADSP::CommandListProcessor& processor) { +void MixCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count)}; auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, @@ -63,8 +63,8 @@ void MixCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool MixCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool MixCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/mix.h b/src/audio_core/renderer/command/mix/mix.h index 0201cf171..64c812382 100644 --- a/src/audio_core/renderer/command/mix/mix.h +++ b/src/audio_core/renderer/command/mix/mix.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume * applied to the input. @@ -24,14 +25,14 @@ struct MixCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -39,7 +40,7 @@ struct MixCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Fixed point precision u8 precision; @@ -51,4 +52,4 @@ struct MixCommand : ICommand { f32 volume; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/mix_ramp.cpp b/src/audio_core/renderer/command/mix/mix_ramp.cpp index d67123cd8..2f6500da5 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp.cpp +++ b/src/audio_core/renderer/command/mix/mix_ramp.cpp @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/mix_ramp.h" #include "common/fixed_point.h" #include "common/logging/log.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { template <size_t Q> s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_, @@ -33,7 +33,8 @@ s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 vo template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, f32, f32, u32); template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, f32, f32, u32); -void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { +void MixRampCommand::Dump(const AudioRenderer::CommandListProcessor& processor, + std::string& string) { const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; string += fmt::format("MixRampCommand"); string += fmt::format("\n\tinput {:02X}", input_index); @@ -44,7 +45,7 @@ void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::stri string += "\n"; } -void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) { +void MixRampCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count)}; auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, @@ -75,8 +76,8 @@ void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool MixRampCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool MixRampCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/mix_ramp.h b/src/audio_core/renderer/command/mix/mix_ramp.h index 52f74a273..92209b53a 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp.h +++ b/src/audio_core/renderer/command/mix/mix_ramp.h @@ -9,11 +9,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume * applied to the input, and volume ramping to smooth out the transition. @@ -25,14 +26,14 @@ struct MixRampCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,7 +41,7 @@ struct MixRampCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Fixed point precision u8 precision; @@ -70,4 +71,4 @@ template <size_t Q> s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, f32 volume_, f32 ramp_, u32 sample_count); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp index 43dbef9fc..64138a9bf 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp +++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp @@ -1,13 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/mix_ramp.h" #include "audio_core/renderer/command/mix/mix_ramp_grouped.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { +void MixRampGroupedCommand::Dump(const AudioRenderer::CommandListProcessor& processor, + std::string& string) { string += "MixRampGroupedCommand"; for (u32 i = 0; i < buffer_count; i++) { string += fmt::format("\n\t{}", i); @@ -21,7 +22,7 @@ void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, st } } -void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor) { +void MixRampGroupedCommand::Process(const AudioRenderer::CommandListProcessor& processor) { std::span<s32> prev_samples = {reinterpret_cast<s32*>(previous_samples), MaxMixBuffers}; for (u32 i = 0; i < buffer_count; i++) { @@ -58,8 +59,8 @@ void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor) } } -bool MixRampGroupedCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool MixRampGroupedCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h index 3b0ce67ef..9621e42a3 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h +++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h @@ -9,11 +9,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for mixing multiple input mix buffers to multiple output mix buffers, with * a volume applied to the input, and volume ramping to smooth out the transition. @@ -25,14 +26,14 @@ struct MixRampGroupedCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,7 +41,7 @@ struct MixRampGroupedCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Fixed point precision u8 precision; @@ -58,4 +59,4 @@ struct MixRampGroupedCommand : ICommand { CpuAddr previous_samples; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/volume.cpp b/src/audio_core/renderer/command/mix/volume.cpp index b045fb062..92baf6cc3 100644 --- a/src/audio_core/renderer/command/mix/volume.cpp +++ b/src/audio_core/renderer/command/mix/volume.cpp @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/volume.h" #include "common/fixed_point.h" #include "common/logging/log.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Apply volume to the input mix buffer, saving to the output buffer. * @@ -29,7 +29,7 @@ static void ApplyUniformGain(std::span<s32> output, std::span<const s32> input, } } -void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void VolumeCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("VolumeCommand"); string += fmt::format("\n\tinput {:02X}", input_index); @@ -38,7 +38,7 @@ void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc string += "\n"; } -void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) { +void VolumeCommand::Process(const AudioRenderer::CommandListProcessor& processor) { // If input and output buffers are the same, and the volume is 1.0f, this won't do // anything, so just skip. if (input_index == output_index && volume == 1.0f) { @@ -65,8 +65,8 @@ void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool VolumeCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool VolumeCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/volume.h b/src/audio_core/renderer/command/mix/volume.h index 6ae9fb794..fbb8156ca 100644 --- a/src/audio_core/renderer/command/mix/volume.h +++ b/src/audio_core/renderer/command/mix/volume.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for applying volume to a mix buffer. */ @@ -23,14 +24,14 @@ struct VolumeCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -38,7 +39,7 @@ struct VolumeCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Fixed point precision u8 precision; @@ -50,4 +51,4 @@ struct VolumeCommand : ICommand { f32 volume; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/volume_ramp.cpp b/src/audio_core/renderer/command/mix/volume_ramp.cpp index 424307148..fdc751957 100644 --- a/src/audio_core/renderer/command/mix/volume_ramp.cpp +++ b/src/audio_core/renderer/command/mix/volume_ramp.cpp @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/mix/volume_ramp.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Apply volume with ramping to the input mix buffer, saving to the output buffer. * @@ -38,7 +38,8 @@ static void ApplyLinearEnvelopeGain(std::span<s32> output, std::span<const s32> } } -void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { +void VolumeRampCommand::Dump(const AudioRenderer::CommandListProcessor& processor, + std::string& string) { const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; string += fmt::format("VolumeRampCommand"); string += fmt::format("\n\tinput {:02X}", input_index); @@ -49,7 +50,7 @@ void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::s string += "\n"; } -void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) { +void VolumeRampCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, processor.sample_count)}; auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, @@ -77,8 +78,8 @@ void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool VolumeRampCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool VolumeRampCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/mix/volume_ramp.h b/src/audio_core/renderer/command/mix/volume_ramp.h index 77b61547e..d9794fb95 100644 --- a/src/audio_core/renderer/command/mix/volume_ramp.h +++ b/src/audio_core/renderer/command/mix/volume_ramp.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for applying volume to a mix buffer, with ramping for the volume to smooth * out the transition. @@ -24,14 +25,14 @@ struct VolumeRampCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -39,7 +40,7 @@ struct VolumeRampCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Fixed point precision u8 precision; @@ -53,4 +54,4 @@ struct VolumeRampCommand : ICommand { f32 volume; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/performance/performance.cpp b/src/audio_core/renderer/command/performance/performance.cpp index 4a881547f..f0cfcc9fd 100644 --- a/src/audio_core/renderer/command/performance/performance.cpp +++ b/src/audio_core/renderer/command/performance/performance.cpp @@ -1,25 +1,25 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/performance/performance.h" #include "core/core.h" #include "core/core_timing.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void PerformanceCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void PerformanceCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("PerformanceCommand\n\tstate {}\n", static_cast<u32>(state)); } -void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) { +void PerformanceCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto base{entry_address.translated_address}; if (state == PerformanceState::Start) { auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)}; *start_time_ptr = - static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - - processor.current_processing_time); + static_cast<u32>(processor.system->CoreTiming().GetGlobalTimeUs().count() - + processor.start_time - processor.current_processing_time); } else if (state == PerformanceState::Stop) { auto processed_time_ptr{ reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)}; @@ -27,14 +27,14 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) { reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)}; *processed_time_ptr = - static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - - processor.current_processing_time); + static_cast<u32>(processor.system->CoreTiming().GetGlobalTimeUs().count() - + processor.start_time - processor.current_processing_time); (*entry_count_ptr)++; } } -bool PerformanceCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool PerformanceCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/performance/performance.h b/src/audio_core/renderer/command/performance/performance.h index 11a7d6c08..522e51e34 100644 --- a/src/audio_core/renderer/command/performance/performance.h +++ b/src/audio_core/renderer/command/performance/performance.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/performance/performance_manager.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for writing AudioRenderer performance metrics back to the sysmodule. */ @@ -25,14 +26,14 @@ struct PerformanceCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,7 +41,7 @@ struct PerformanceCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// State of the performance PerformanceState state; @@ -48,4 +49,4 @@ struct PerformanceCommand : ICommand { PerformanceEntryAddresses entry_address; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp index 1fd90308a..f9b289887 100644 --- a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp +++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/resample/downmix_6ch_to_2ch.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void DownMix6chTo2chCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void DownMix6chTo2chCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("DownMix6chTo2chCommand\n\tinputs: "); for (u32 i = 0; i < MaxChannels; i++) { string += fmt::format("{:02X}, ", inputs[i]); @@ -19,7 +19,7 @@ void DownMix6chTo2chCommand::Dump([[maybe_unused]] const ADSP::CommandListProces string += "\n"; } -void DownMix6chTo2chCommand::Process(const ADSP::CommandListProcessor& processor) { +void DownMix6chTo2chCommand::Process(const AudioRenderer::CommandListProcessor& processor) { auto in_front_left{ processor.mix_buffers.subspan(inputs[0] * processor.sample_count, processor.sample_count)}; auto in_front_right{ @@ -67,8 +67,8 @@ void DownMix6chTo2chCommand::Process(const ADSP::CommandListProcessor& processor std::memset(out_back_right.data(), 0, out_back_right.size_bytes()); } -bool DownMix6chTo2chCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool DownMix6chTo2chCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h index dc133a73b..96cbc5506 100644 --- a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h +++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h @@ -9,11 +9,12 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for downmixing 6 channels to 2. * Channel layout (SMPTE): @@ -31,14 +32,14 @@ struct DownMix6chTo2chCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -46,7 +47,7 @@ struct DownMix6chTo2chCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Input mix buffer offsets for each channel std::array<s16, MaxChannels> inputs; @@ -56,4 +57,4 @@ struct DownMix6chTo2chCommand : ICommand { std::array<Common::FixedPoint<48, 16>, 4> down_mix_coeff; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/resample/resample.cpp b/src/audio_core/renderer/command/resample/resample.cpp index 070c9d2b8..51f4ba39e 100644 --- a/src/audio_core/renderer/command/resample/resample.cpp +++ b/src/audio_core/renderer/command/resample/resample.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/command/resample/resample.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input, const Common::FixedPoint<49, 15>& sample_rate_ratio, @@ -880,4 +880,4 @@ void Resample(std::span<s32> output, std::span<const s16> input, } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/resample/resample.h b/src/audio_core/renderer/command/resample/resample.h index ba9209b82..134aff0c9 100644 --- a/src/audio_core/renderer/command/resample/resample.h +++ b/src/audio_core/renderer/command/resample/resample.h @@ -9,7 +9,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Resample an input buffer into an output buffer, according to the sample_rate_ratio. * @@ -26,4 +26,4 @@ void Resample(std::span<s32> output, std::span<const s16> input, const Common::FixedPoint<49, 15>& sample_rate_ratio, Common::FixedPoint<49, 15>& fraction, u32 samples_to_write, SrcQuality src_quality); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/resample/upsample.cpp b/src/audio_core/renderer/command/resample/upsample.cpp index 86ddee1a4..691d70390 100644 --- a/src/audio_core/renderer/command/resample/upsample.cpp +++ b/src/audio_core/renderer/command/resample/upsample.cpp @@ -3,11 +3,11 @@ #include <array> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/resample/upsample.h" #include "audio_core/renderer/upsampler/upsampler_info.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Upsampling impl. Input must be 8K, 16K or 32K, output is 48K. * @@ -198,7 +198,7 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input, } } -auto UpsampleCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +auto UpsampleCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) -> void { string += fmt::format("UpsampleCommand\n\tsource_sample_count {} source_sample_rate {}", source_sample_count, source_sample_rate); @@ -213,7 +213,7 @@ auto UpsampleCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& pr string += "\n"; } -void UpsampleCommand::Process(const ADSP::CommandListProcessor& processor) { +void UpsampleCommand::Process(const AudioRenderer::CommandListProcessor& processor) { const auto info{reinterpret_cast<UpsamplerInfo*>(upsampler_info)}; const auto input_count{std::min(info->input_count, buffer_count)}; const std::span<const s16> inputs_{reinterpret_cast<const s16*>(inputs), input_count}; @@ -234,8 +234,8 @@ void UpsampleCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool UpsampleCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool UpsampleCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/resample/upsample.h b/src/audio_core/renderer/command/resample/upsample.h index bfc94e8af..877271ba9 100644 --- a/src/audio_core/renderer/command/resample/upsample.h +++ b/src/audio_core/renderer/command/resample/upsample.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for upsampling a mix buffer to 48Khz. * Input must be 8Khz, 16Khz or 32Khz, and output will be 48Khz. @@ -24,14 +25,14 @@ struct UpsampleCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -39,7 +40,7 @@ struct UpsampleCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Pointer to the output samples buffer. CpuAddr samples_buffer; @@ -57,4 +58,4 @@ struct UpsampleCommand : ICommand { CpuAddr upsampler_info; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp index e2ce59792..e056d15a6 100644 --- a/src/audio_core/renderer/command/sink/circular_buffer.cpp +++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp @@ -3,14 +3,14 @@ #include <vector> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/sink/circular_buffer.h" #include "core/memory.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void CircularBufferSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, - std::string& string) { +void CircularBufferSinkCommand::Dump( + [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format( "CircularBufferSinkCommand\n\tinput_count {} ring size {:04X} ring pos {:04X}\n\tinputs: ", input_count, size, pos); @@ -20,7 +20,7 @@ void CircularBufferSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListPro string += "\n"; } -void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& processor) { +void CircularBufferSinkCommand::Process(const AudioRenderer::CommandListProcessor& processor) { constexpr s32 min{std::numeric_limits<s16>::min()}; constexpr s32 max{std::numeric_limits<s16>::max()}; @@ -41,8 +41,8 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces } } -bool CircularBufferSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool CircularBufferSinkCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/sink/circular_buffer.h b/src/audio_core/renderer/command/sink/circular_buffer.h index e7d5be26e..a3234a406 100644 --- a/src/audio_core/renderer/command/sink/circular_buffer.h +++ b/src/audio_core/renderer/command/sink/circular_buffer.h @@ -8,11 +8,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for sinking samples to a circular buffer. */ @@ -23,14 +24,14 @@ struct CircularBufferSinkCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -38,7 +39,7 @@ struct CircularBufferSinkCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Number of input mix buffers u32 input_count; @@ -52,4 +53,4 @@ struct CircularBufferSinkCommand : ICommand { u32 pos; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp index 5f74dd7ad..3480ed475 100644 --- a/src/audio_core/renderer/command/sink/device.cpp +++ b/src/audio_core/renderer/command/sink/device.cpp @@ -3,13 +3,13 @@ #include <algorithm> -#include "audio_core/renderer/adsp/command_list_processor.h" +#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" #include "audio_core/renderer/command/sink/device.h" #include "audio_core/sink/sink.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { -void DeviceSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, +void DeviceSinkCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) { string += fmt::format("DeviceSinkCommand\n\t{} session {} input_count {}\n\tinputs: ", std::string_view(name), session_id, input_count); @@ -19,7 +19,7 @@ void DeviceSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& string += "\n"; } -void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { +void DeviceSinkCommand::Process(const AudioRenderer::CommandListProcessor& processor) { constexpr s32 min = std::numeric_limits<s16>::min(); constexpr s32 max = std::numeric_limits<s16>::max(); @@ -51,8 +51,8 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { } } -bool DeviceSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { +bool DeviceSinkCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { return true; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/command/sink/device.h b/src/audio_core/renderer/command/sink/device.h index 1099bcf8c..385b51ecc 100644 --- a/src/audio_core/renderer/command/sink/device.h +++ b/src/audio_core/renderer/command/sink/device.h @@ -10,11 +10,12 @@ #include "audio_core/renderer/command/icommand.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP::AudioRenderer { class CommandListProcessor; } +namespace AudioCore::Renderer { + /** * AudioRenderer command for sinking samples to an output device. */ @@ -25,14 +26,14 @@ struct DeviceSinkCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @param string - The string to print into. */ - void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; + void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override; /** * Process this command. * * @param processor - The CommandListProcessor processing this command. */ - void Process(const ADSP::CommandListProcessor& processor) override; + void Process(const AudioRenderer::CommandListProcessor& processor) override; /** * Verify this command's data is valid. @@ -40,7 +41,7 @@ struct DeviceSinkCommand : ICommand { * @param processor - The CommandListProcessor processing this command. * @return True if the command is valid, otherwise false. */ - bool Verify(const ADSP::CommandListProcessor& processor) override; + bool Verify(const AudioRenderer::CommandListProcessor& processor) override; /// Device name char name[0x100]; @@ -54,4 +55,4 @@ struct DeviceSinkCommand : ICommand { std::array<s16, MaxChannels> inputs; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/aux_.cpp b/src/audio_core/renderer/effect/aux_.cpp index 51e780ef1..1c1411eff 100644 --- a/src/audio_core/renderer/effect/aux_.cpp +++ b/src/audio_core/renderer/effect/aux_.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/aux_.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -90,4 +90,4 @@ CpuAddr AuxInfo::GetWorkbuffer(s32 index) { return workbuffers[index].GetReference(true); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/aux_.h b/src/audio_core/renderer/effect/aux_.h index 4d3d9e3d9..c5b3058da 100644 --- a/src/audio_core/renderer/effect/aux_.h +++ b/src/audio_core/renderer/effect/aux_.h @@ -9,7 +9,7 @@ #include "audio_core/renderer/effect/effect_info_base.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Auxiliary Buffer used for Aux commands. * Send and return buffers are available (names from the game's perspective). @@ -120,4 +120,4 @@ public: CpuAddr GetWorkbuffer(s32 index) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/biquad_filter.cpp b/src/audio_core/renderer/effect/biquad_filter.cpp index a1efb3231..08161d840 100644 --- a/src/audio_core/renderer/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/effect/biquad_filter.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/biquad_filter.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -49,4 +49,4 @@ void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {} void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/biquad_filter.h b/src/audio_core/renderer/effect/biquad_filter.h index f53fd5bab..5a22899ab 100644 --- a/src/audio_core/renderer/effect/biquad_filter.h +++ b/src/audio_core/renderer/effect/biquad_filter.h @@ -9,7 +9,7 @@ #include "audio_core/renderer/effect/effect_info_base.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class BiquadFilterInfo : public EffectInfoBase { public: @@ -76,4 +76,4 @@ public: void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/buffer_mixer.cpp b/src/audio_core/renderer/effect/buffer_mixer.cpp index 9c8877f01..826e246ec 100644 --- a/src/audio_core/renderer/effect/buffer_mixer.cpp +++ b/src/audio_core/renderer/effect/buffer_mixer.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/buffer_mixer.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -46,4 +46,4 @@ void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {} void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/buffer_mixer.h b/src/audio_core/renderer/effect/buffer_mixer.h index 23eed4a8b..0c01ef38d 100644 --- a/src/audio_core/renderer/effect/buffer_mixer.h +++ b/src/audio_core/renderer/effect/buffer_mixer.h @@ -9,7 +9,7 @@ #include "audio_core/renderer/effect/effect_info_base.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class BufferMixerInfo : public EffectInfoBase { public: @@ -72,4 +72,4 @@ public: void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/capture.cpp b/src/audio_core/renderer/effect/capture.cpp index 3f038efdb..dfa062a59 100644 --- a/src/audio_core/renderer/effect/capture.cpp +++ b/src/audio_core/renderer/effect/capture.cpp @@ -4,7 +4,7 @@ #include "audio_core/renderer/effect/aux_.h" #include "audio_core/renderer/effect/capture.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -79,4 +79,4 @@ CpuAddr CaptureInfo::GetWorkbuffer(s32 index) { return workbuffers[index].GetReference(true); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/capture.h b/src/audio_core/renderer/effect/capture.h index 6fbed8e6b..cbe71e22a 100644 --- a/src/audio_core/renderer/effect/capture.h +++ b/src/audio_core/renderer/effect/capture.h @@ -9,7 +9,7 @@ #include "audio_core/renderer/effect/effect_info_base.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class CaptureInfo : public EffectInfoBase { public: @@ -62,4 +62,4 @@ public: CpuAddr GetWorkbuffer(s32 index) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/compressor.cpp b/src/audio_core/renderer/effect/compressor.cpp index 220ae02f9..fea0aefcf 100644 --- a/src/audio_core/renderer/effect/compressor.cpp +++ b/src/audio_core/renderer/effect/compressor.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/compressor.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {} @@ -37,4 +37,4 @@ CpuAddr CompressorInfo::GetWorkbuffer(s32 index) { return GetSingleBuffer(index); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/compressor.h b/src/audio_core/renderer/effect/compressor.h index 019a5ae58..cda55c284 100644 --- a/src/audio_core/renderer/effect/compressor.h +++ b/src/audio_core/renderer/effect/compressor.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class CompressorInfo : public EffectInfoBase { public: @@ -103,4 +103,4 @@ public: CpuAddr GetWorkbuffer(s32 index) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/delay.cpp b/src/audio_core/renderer/effect/delay.cpp index d9853efd9..e038d4498 100644 --- a/src/audio_core/renderer/effect/delay.cpp +++ b/src/audio_core/renderer/effect/delay.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/delay.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -90,4 +90,4 @@ CpuAddr DelayInfo::GetWorkbuffer(s32 index) { return GetSingleBuffer(index); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/delay.h b/src/audio_core/renderer/effect/delay.h index accc42a06..47417fbc6 100644 --- a/src/audio_core/renderer/effect/delay.h +++ b/src/audio_core/renderer/effect/delay.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class DelayInfo : public EffectInfoBase { public: @@ -132,4 +132,4 @@ public: CpuAddr GetWorkbuffer(s32 index) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/effect_context.cpp b/src/audio_core/renderer/effect/effect_context.cpp index 74c7801c9..00f6d7822 100644 --- a/src/audio_core/renderer/effect/effect_context.cpp +++ b/src/audio_core/renderer/effect/effect_context.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/effect_context.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void EffectContext::Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, std::span<EffectResultState> result_states_cpu_, @@ -38,4 +38,4 @@ void EffectContext::UpdateStateByDspShared() { } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/effect_context.h b/src/audio_core/renderer/effect/effect_context.h index 8f6d6e7d8..8364c5521 100644 --- a/src/audio_core/renderer/effect/effect_context.h +++ b/src/audio_core/renderer/effect/effect_context.h @@ -9,7 +9,7 @@ #include "audio_core/renderer/effect/effect_result_state.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class EffectContext { public: @@ -72,4 +72,4 @@ private: size_t dsp_state_count{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h index dbdccf278..b49503409 100644 --- a/src/audio_core/renderer/effect/effect_info_base.h +++ b/src/audio_core/renderer/effect/effect_info_base.h @@ -12,7 +12,7 @@ #include "audio_core/renderer/memory/pool_mapper.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Base of all effects. Holds various data and functions used for all derived effects. * Should not be used directly. @@ -432,4 +432,4 @@ protected: std::array<u8, sizeof(State)> state{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/effect_reset.h b/src/audio_core/renderer/effect/effect_reset.h index 1ea67e334..c9e3b4b78 100644 --- a/src/audio_core/renderer/effect/effect_reset.h +++ b/src/audio_core/renderer/effect/effect_reset.h @@ -14,7 +14,7 @@ #include "audio_core/renderer/effect/reverb.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Reset an effect, and create a new one of the given type. * @@ -68,4 +68,4 @@ static void ResetEffect(EffectInfoBase* effect, const EffectInfoBase::Type type) } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/effect_result_state.h b/src/audio_core/renderer/effect/effect_result_state.h index ae096ad69..f4d4b6086 100644 --- a/src/audio_core/renderer/effect/effect_result_state.h +++ b/src/audio_core/renderer/effect/effect_result_state.h @@ -7,10 +7,10 @@ #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { struct EffectResultState { std::array<u8, 0x80> state; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/i3dl2.cpp b/src/audio_core/renderer/effect/i3dl2.cpp index 960b29cfc..a3c324c1e 100644 --- a/src/audio_core/renderer/effect/i3dl2.cpp +++ b/src/audio_core/renderer/effect/i3dl2.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/i3dl2.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -91,4 +91,4 @@ CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) { return GetSingleBuffer(index); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/i3dl2.h b/src/audio_core/renderer/effect/i3dl2.h index 6e3ffd1d4..e0432b4ae 100644 --- a/src/audio_core/renderer/effect/i3dl2.h +++ b/src/audio_core/renderer/effect/i3dl2.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class I3dl2ReverbInfo : public EffectInfoBase { public: @@ -198,4 +198,4 @@ public: CpuAddr GetWorkbuffer(s32 index) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/light_limiter.cpp b/src/audio_core/renderer/effect/light_limiter.cpp index 1635a952d..9c8ea3c49 100644 --- a/src/audio_core/renderer/effect/light_limiter.cpp +++ b/src/audio_core/renderer/effect/light_limiter.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/light_limiter.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -78,4 +78,4 @@ CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) { return GetSingleBuffer(index); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/light_limiter.h b/src/audio_core/renderer/effect/light_limiter.h index 338d67bbc..7f2ede405 100644 --- a/src/audio_core/renderer/effect/light_limiter.h +++ b/src/audio_core/renderer/effect/light_limiter.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class LightLimiterInfo : public EffectInfoBase { public: @@ -135,4 +135,4 @@ public: CpuAddr GetWorkbuffer(s32 index) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/reverb.cpp b/src/audio_core/renderer/effect/reverb.cpp index 2d32383d0..4da72469a 100644 --- a/src/audio_core/renderer/effect/reverb.cpp +++ b/src/audio_core/renderer/effect/reverb.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/effect/reverb.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { @@ -90,4 +90,4 @@ CpuAddr ReverbInfo::GetWorkbuffer(s32 index) { return GetSingleBuffer(index); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/reverb.h b/src/audio_core/renderer/effect/reverb.h index 6cc345ef6..52a048da6 100644 --- a/src/audio_core/renderer/effect/reverb.h +++ b/src/audio_core/renderer/effect/reverb.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class ReverbInfo : public EffectInfoBase { public: @@ -187,4 +187,4 @@ public: CpuAddr GetWorkbuffer(s32 index) override; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/memory/address_info.h b/src/audio_core/renderer/memory/address_info.h index bb5c930e1..c81ef1b8a 100644 --- a/src/audio_core/renderer/memory/address_info.h +++ b/src/audio_core/renderer/memory/address_info.h @@ -6,7 +6,7 @@ #include "audio_core/renderer/memory/memory_pool_info.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Represents a region of mapped or unmapped memory. @@ -121,4 +121,4 @@ private: CpuAddr dsp_address; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/memory/memory_pool_info.cpp b/src/audio_core/renderer/memory/memory_pool_info.cpp index 9b7824af1..03b44d5f3 100644 --- a/src/audio_core/renderer/memory/memory_pool_info.cpp +++ b/src/audio_core/renderer/memory/memory_pool_info.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/memory/memory_pool_info.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { CpuAddr MemoryPoolInfo::GetCpuAddress() const { return cpu_address; @@ -58,4 +58,4 @@ bool MemoryPoolInfo::IsUsed() const { return in_use; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/memory/memory_pool_info.h b/src/audio_core/renderer/memory/memory_pool_info.h index 80c571bc1..2f9c85184 100644 --- a/src/audio_core/renderer/memory/memory_pool_info.h +++ b/src/audio_core/renderer/memory/memory_pool_info.h @@ -8,7 +8,7 @@ #include "audio_core/common/common.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * CPU pools are mapped in user memory with the supplied process_handle (see PoolMapper). */ @@ -167,4 +167,4 @@ private: bool in_use{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/memory/pool_mapper.cpp b/src/audio_core/renderer/memory/pool_mapper.cpp index 7fd2b5f47..999bb746b 100644 --- a/src/audio_core/renderer/memory/pool_mapper.cpp +++ b/src/audio_core/renderer/memory/pool_mapper.cpp @@ -6,7 +6,7 @@ #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/svc.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { PoolMapper::PoolMapper(u32 process_handle_, bool force_map_) : process_handle{process_handle_}, force_map{force_map_} {} @@ -240,4 +240,4 @@ bool PoolMapper::InitializeSystemPool(MemoryPoolInfo& pool, const u8* memory, } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/memory/pool_mapper.h b/src/audio_core/renderer/memory/pool_mapper.h index 9a691da7a..95ae5d8ea 100644 --- a/src/audio_core/renderer/memory/pool_mapper.h +++ b/src/audio_core/renderer/memory/pool_mapper.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "core/hle/service/audio/errors.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class AddressInfo; /** @@ -176,4 +176,4 @@ private: bool force_map; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp index 3a18ae7c2..c712610bb 100644 --- a/src/audio_core/renderer/mix/mix_context.cpp +++ b/src/audio_core/renderer/mix/mix_context.cpp @@ -7,7 +7,7 @@ #include "audio_core/renderer/splitter/splitter_context.h" #include "common/polyfill_ranges.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void MixContext::Initialize(std::span<MixInfo*> sorted_mix_infos_, std::span<MixInfo> mix_infos_, const u32 count_, std::span<s32> effect_process_order_buffer_, @@ -139,4 +139,4 @@ EdgeMatrix& MixContext::GetEdgeMatrix() { return edge_matrix; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/mix/mix_context.h b/src/audio_core/renderer/mix/mix_context.h index bcd9637da..ce19ec8d6 100644 --- a/src/audio_core/renderer/mix/mix_context.h +++ b/src/audio_core/renderer/mix/mix_context.h @@ -10,7 +10,7 @@ #include "audio_core/renderer/nodes/node_states.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class SplitterContext; /* @@ -121,4 +121,4 @@ private: EdgeMatrix edge_matrix{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/mix/mix_info.cpp b/src/audio_core/renderer/mix/mix_info.cpp index cc18e57ee..5e44bde18 100644 --- a/src/audio_core/renderer/mix/mix_info.cpp +++ b/src/audio_core/renderer/mix/mix_info.cpp @@ -7,7 +7,7 @@ #include "audio_core/renderer/nodes/edge_matrix.h" #include "audio_core/renderer/splitter/splitter_context.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { MixInfo::MixInfo(std::span<s32> effect_order_buffer_, s32 effect_count_, BehaviorInfo& behavior) : effect_order_buffer{effect_order_buffer_}, effect_count{effect_count_}, @@ -117,4 +117,4 @@ bool MixInfo::HasAnyConnection() const { return dst_mix_id != UnusedMixId || dst_splitter_id != UnusedSplitterId; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/mix/mix_info.h b/src/audio_core/renderer/mix/mix_info.h index b5fa4c0c7..7005daa4f 100644 --- a/src/audio_core/renderer/mix/mix_info.h +++ b/src/audio_core/renderer/mix/mix_info.h @@ -9,7 +9,7 @@ #include "audio_core/common/common.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class EdgeMatrix; class SplitterContext; class EffectContext; @@ -121,4 +121,4 @@ public: const bool long_size_pre_delay_supported; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/nodes/bit_array.h b/src/audio_core/renderer/nodes/bit_array.h index b0d53cd51..d8a2d09d0 100644 --- a/src/audio_core/renderer/nodes/bit_array.h +++ b/src/audio_core/renderer/nodes/bit_array.h @@ -7,7 +7,7 @@ #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Represents an array of bits used for nodes and edges for the mixing graph. */ @@ -22,4 +22,4 @@ struct BitArray { u32 size{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/nodes/edge_matrix.cpp b/src/audio_core/renderer/nodes/edge_matrix.cpp index 5573f33b9..c28773b22 100644 --- a/src/audio_core/renderer/nodes/edge_matrix.cpp +++ b/src/audio_core/renderer/nodes/edge_matrix.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/nodes/edge_matrix.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void EdgeMatrix::Initialize([[maybe_unused]] std::span<u8> buffer, [[maybe_unused]] const u64 node_buffer_size, const u32 count_) { @@ -35,4 +35,4 @@ u32 EdgeMatrix::GetNodeCount() const { return count; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/nodes/edge_matrix.h b/src/audio_core/renderer/nodes/edge_matrix.h index 27a20e43e..0271c23b1 100644 --- a/src/audio_core/renderer/nodes/edge_matrix.h +++ b/src/audio_core/renderer/nodes/edge_matrix.h @@ -9,7 +9,7 @@ #include "common/alignment.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * An edge matrix, holding the connections for each node to every other node in the graph. */ @@ -79,4 +79,4 @@ private: u32 count; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp index b7a44a54c..028a58041 100644 --- a/src/audio_core/renderer/nodes/node_states.cpp +++ b/src/audio_core/renderer/nodes/node_states.cpp @@ -4,7 +4,7 @@ #include "audio_core/renderer/nodes/node_states.h" #include "common/logging/log.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void NodeStates::Initialize(std::span<u8> buffer_, [[maybe_unused]] const u64 node_buffer_size, const u32 count) { @@ -138,4 +138,4 @@ std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls( return {results.rbegin(), result_pos}; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h index e768cd4b5..991a82841 100644 --- a/src/audio_core/renderer/nodes/node_states.h +++ b/src/audio_core/renderer/nodes/node_states.h @@ -10,7 +10,7 @@ #include "common/alignment.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Graph utility functions for sorting and getting results from the DAG. */ @@ -192,4 +192,4 @@ private: Stack stack{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/detail_aspect.cpp b/src/audio_core/renderer/performance/detail_aspect.cpp index f6405937f..ef8b47cee 100644 --- a/src/audio_core/renderer/performance/detail_aspect.cpp +++ b/src/audio_core/renderer/performance/detail_aspect.cpp @@ -5,7 +5,7 @@ #include "audio_core/renderer/command/command_generator.h" #include "audio_core/renderer/performance/detail_aspect.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { DetailAspect::DetailAspect(CommandGenerator& command_generator_, const PerformanceEntryType entry_type, const s32 node_id_, @@ -22,4 +22,4 @@ DetailAspect::DetailAspect(CommandGenerator& command_generator_, } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/detail_aspect.h b/src/audio_core/renderer/performance/detail_aspect.h index 736c331b9..0bd7f80c8 100644 --- a/src/audio_core/renderer/performance/detail_aspect.h +++ b/src/audio_core/renderer/performance/detail_aspect.h @@ -7,7 +7,7 @@ #include "audio_core/renderer/performance/performance_manager.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class CommandGenerator; /** @@ -29,4 +29,4 @@ public: s32 node_id; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/entry_aspect.cpp b/src/audio_core/renderer/performance/entry_aspect.cpp index dd4165803..c9241a639 100644 --- a/src/audio_core/renderer/performance/entry_aspect.cpp +++ b/src/audio_core/renderer/performance/entry_aspect.cpp @@ -5,7 +5,7 @@ #include "audio_core/renderer/command/command_generator.h" #include "audio_core/renderer/performance/entry_aspect.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { EntryAspect::EntryAspect(CommandGenerator& command_generator_, const PerformanceEntryType type, const s32 node_id_) @@ -20,4 +20,4 @@ EntryAspect::EntryAspect(CommandGenerator& command_generator_, const Performance } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/entry_aspect.h b/src/audio_core/renderer/performance/entry_aspect.h index 14c9e3baf..f99287d68 100644 --- a/src/audio_core/renderer/performance/entry_aspect.h +++ b/src/audio_core/renderer/performance/entry_aspect.h @@ -7,7 +7,7 @@ #include "audio_core/renderer/performance/performance_manager.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class CommandGenerator; /** @@ -28,4 +28,4 @@ public: s32 node_id; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/performance_detail.h b/src/audio_core/renderer/performance/performance_detail.h index f603b9026..2b0cf9422 100644 --- a/src/audio_core/renderer/performance/performance_detail.h +++ b/src/audio_core/renderer/performance/performance_detail.h @@ -6,7 +6,7 @@ #include "audio_core/renderer/performance/performance_entry.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { enum class PerformanceDetailType : u8 { Invalid, @@ -47,4 +47,4 @@ struct PerformanceDetailVersion2 { static_assert(sizeof(PerformanceDetailVersion2) == 0x18, "PerformanceDetailVersion2 has the wrong size!"); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/performance_entry.h b/src/audio_core/renderer/performance/performance_entry.h index d6b1158db..dbd6053a5 100644 --- a/src/audio_core/renderer/performance/performance_entry.h +++ b/src/audio_core/renderer/performance/performance_entry.h @@ -5,7 +5,7 @@ #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { enum class PerformanceEntryType : u8 { Invalid, @@ -34,4 +34,4 @@ struct PerformanceEntryVersion2 { static_assert(sizeof(PerformanceEntryVersion2) == 0x18, "PerformanceEntryVersion2 has the wrong size!"); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/performance_entry_addresses.h b/src/audio_core/renderer/performance/performance_entry_addresses.h index e381d765c..51eee975f 100644 --- a/src/audio_core/renderer/performance/performance_entry_addresses.h +++ b/src/audio_core/renderer/performance/performance_entry_addresses.h @@ -5,7 +5,7 @@ #include "audio_core/common/common.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { struct PerformanceEntryAddresses { CpuAddr translated_address; @@ -14,4 +14,4 @@ struct PerformanceEntryAddresses { CpuAddr entry_processed_time_offset; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/performance_frame_header.h b/src/audio_core/renderer/performance/performance_frame_header.h index b1848284e..24e4989f8 100644 --- a/src/audio_core/renderer/performance/performance_frame_header.h +++ b/src/audio_core/renderer/performance/performance_frame_header.h @@ -5,7 +5,7 @@ #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { struct PerformanceFrameHeaderVersion1 { /* 0x00 */ u32 magic; // "PERF" @@ -33,4 +33,4 @@ struct PerformanceFrameHeaderVersion2 { static_assert(sizeof(PerformanceFrameHeaderVersion2) == 0x30, "PerformanceFrameHeaderVersion2 has the wrong size!"); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp index 8aa0f5ed0..ce736db71 100644 --- a/src/audio_core/renderer/performance/performance_manager.cpp +++ b/src/audio_core/renderer/performance/performance_manager.cpp @@ -6,7 +6,7 @@ #include "audio_core/renderer/performance/performance_manager.h" #include "common/common_funcs.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void PerformanceManager::CreateImpl(const size_t version) { switch (version) { @@ -643,4 +643,4 @@ void PerformanceManagerImpl<PerformanceVersion::Version2, PerformanceFrameHeader target_node_id = target_node_id_; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/performance/performance_manager.h b/src/audio_core/renderer/performance/performance_manager.h index b65caa9b6..ffd0fa1fb 100644 --- a/src/audio_core/renderer/performance/performance_manager.h +++ b/src/audio_core/renderer/performance/performance_manager.h @@ -14,7 +14,7 @@ #include "audio_core/renderer/performance/performance_frame_header.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class BehaviorInfo; class MemoryPoolInfo; @@ -272,4 +272,4 @@ private: PerformanceVersion version{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp b/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp index d91f10402..0ede02b6b 100644 --- a/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp +++ b/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp @@ -5,7 +5,7 @@ #include "audio_core/renderer/sink/circular_buffer_sink_info.h" #include "audio_core/renderer/upsampler/upsampler_manager.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { CircularBufferSinkInfo::CircularBufferSinkInfo() { state.fill(0); @@ -73,4 +73,4 @@ void CircularBufferSinkInfo::UpdateForCommandGeneration() { } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/circular_buffer_sink_info.h b/src/audio_core/renderer/sink/circular_buffer_sink_info.h index 3356213ea..d4e61d641 100644 --- a/src/audio_core/renderer/sink/circular_buffer_sink_info.h +++ b/src/audio_core/renderer/sink/circular_buffer_sink_info.h @@ -6,7 +6,7 @@ #include "audio_core/renderer/sink/sink_info_base.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Info for a circular buffer sink. */ @@ -38,4 +38,4 @@ public: static_assert(sizeof(CircularBufferSinkInfo) <= sizeof(SinkInfoBase), "CircularBufferSinkInfo is too large!"); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/device_sink_info.cpp b/src/audio_core/renderer/sink/device_sink_info.cpp index b7b3d6f1d..2de05e38e 100644 --- a/src/audio_core/renderer/sink/device_sink_info.cpp +++ b/src/audio_core/renderer/sink/device_sink_info.cpp @@ -4,7 +4,7 @@ #include "audio_core/renderer/sink/device_sink_info.h" #include "audio_core/renderer/upsampler/upsampler_manager.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { DeviceSinkInfo::DeviceSinkInfo() { state.fill(0); @@ -54,4 +54,4 @@ void DeviceSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStatus& out_ void DeviceSinkInfo::UpdateForCommandGeneration() {} -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/device_sink_info.h b/src/audio_core/renderer/sink/device_sink_info.h index a1c441454..7974ae820 100644 --- a/src/audio_core/renderer/sink/device_sink_info.h +++ b/src/audio_core/renderer/sink/device_sink_info.h @@ -6,7 +6,7 @@ #include "audio_core/renderer/sink/sink_info_base.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Info for a device sink. */ @@ -37,4 +37,4 @@ public: }; static_assert(sizeof(DeviceSinkInfo) <= sizeof(SinkInfoBase), "DeviceSinkInfo is too large!"); -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/sink_context.cpp b/src/audio_core/renderer/sink/sink_context.cpp index 634bc1cf9..a4f9cac21 100644 --- a/src/audio_core/renderer/sink/sink_context.cpp +++ b/src/audio_core/renderer/sink/sink_context.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/sink/sink_context.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void SinkContext::Initialize(std::span<SinkInfoBase> sink_infos_, const u32 sink_count_) { sink_infos = sink_infos_; @@ -18,4 +18,4 @@ u32 SinkContext::GetCount() const { return sink_count; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/sink_context.h b/src/audio_core/renderer/sink/sink_context.h index 185572e29..66925b48e 100644 --- a/src/audio_core/renderer/sink/sink_context.h +++ b/src/audio_core/renderer/sink/sink_context.h @@ -8,7 +8,7 @@ #include "audio_core/renderer/sink/sink_info_base.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Manages output sinks. */ @@ -44,4 +44,4 @@ private: u32 sink_count{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/sink_info_base.cpp b/src/audio_core/renderer/sink/sink_info_base.cpp index 4279beaa0..8a064f15a 100644 --- a/src/audio_core/renderer/sink/sink_info_base.cpp +++ b/src/audio_core/renderer/sink/sink_info_base.cpp @@ -4,7 +4,7 @@ #include "audio_core/renderer/memory/pool_mapper.h" #include "audio_core/renderer/sink/sink_info_base.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { void SinkInfoBase::CleanUp() { type = Type::Invalid; @@ -48,4 +48,4 @@ u8* SinkInfoBase::GetParameter() { return parameter.data(); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/sink/sink_info_base.h b/src/audio_core/renderer/sink/sink_info_base.h index a1b855f20..e10d1cb38 100644 --- a/src/audio_core/renderer/sink/sink_info_base.h +++ b/src/audio_core/renderer/sink/sink_info_base.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { struct UpsamplerInfo; class PoolMapper; @@ -174,4 +174,4 @@ protected: parameter{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp index 7a23ba43f..686150ea6 100644 --- a/src/audio_core/renderer/splitter/splitter_context.cpp +++ b/src/audio_core/renderer/splitter/splitter_context.cpp @@ -7,7 +7,7 @@ #include "audio_core/renderer/splitter/splitter_context.h" #include "common/alignment.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id, const s32 destination_id) { @@ -214,4 +214,4 @@ u64 SplitterContext::CalcWorkBufferSize(const BehaviorInfo& behavior, return size; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h index 1a63db1d3..556e6dcc3 100644 --- a/src/audio_core/renderer/splitter/splitter_context.h +++ b/src/audio_core/renderer/splitter/splitter_context.h @@ -13,7 +13,7 @@ namespace AudioCore { struct AudioRendererParameterInternal; class WorkbufferAllocator; -namespace AudioRenderer { +namespace Renderer { class BehaviorInfo; /** @@ -185,5 +185,5 @@ private: bool splitter_bug_fixed{}; }; -} // namespace AudioRenderer +} // namespace Renderer } // namespace AudioCore diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp index b27d44896..5ec37e48e 100644 --- a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/splitter/splitter_destinations_data.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { SplitterDestinationData::SplitterDestinationData(const s32 id_) : id{id_} {} @@ -84,4 +84,4 @@ void SplitterDestinationData::SetNext(SplitterDestinationData* next_) { next = next_; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.h b/src/audio_core/renderer/splitter/splitter_destinations_data.h index d55ce0ad3..90edfc667 100644 --- a/src/audio_core/renderer/splitter/splitter_destinations_data.h +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.h @@ -9,7 +9,7 @@ #include "audio_core/common/common.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Represents a mixing node, can be connected to a previous and next destination forming a chain * that a certain mix buffer will pass through to output. @@ -132,4 +132,4 @@ private: bool need_update{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_info.cpp b/src/audio_core/renderer/splitter/splitter_info.cpp index 1aee6720b..beb5b7f19 100644 --- a/src/audio_core/renderer/splitter/splitter_info.cpp +++ b/src/audio_core/renderer/splitter/splitter_info.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/splitter/splitter_info.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { SplitterInfo::SplitterInfo(const s32 id_) : id{id_} {} @@ -76,4 +76,4 @@ void SplitterInfo::SetDestinations(SplitterDestinationData* destinations_) { destinations = destinations_; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_info.h b/src/audio_core/renderer/splitter/splitter_info.h index b0ad01fe0..c1e4c2df1 100644 --- a/src/audio_core/renderer/splitter/splitter_info.h +++ b/src/audio_core/renderer/splitter/splitter_info.h @@ -6,7 +6,7 @@ #include "audio_core/renderer/splitter/splitter_destinations_data.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Represents a splitter, wraps multiple output destinations to split an input mix into. */ @@ -104,4 +104,4 @@ private: u32 channel_count{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index 6e07baa54..d29754634 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -4,12 +4,13 @@ #include <chrono> #include <span> +#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" +#include "audio_core/adsp/apps/audio_renderer/command_buffer.h" #include "audio_core/audio_core.h" #include "audio_core/common/audio_renderer_parameter.h" #include "audio_core/common/common.h" #include "audio_core/common/feature_support.h" #include "audio_core/common/workbuffer_allocator.h" -#include "audio_core/renderer/adsp/adsp.h" #include "audio_core/renderer/behavior/info_updater.h" #include "audio_core/renderer/command/command_buffer.h" #include "audio_core/renderer/command/command_generator.h" @@ -34,7 +35,7 @@ #include "core/hle/kernel/k_transfer_memory.h" #include "core/memory.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) { BehaviorInfo behavior; @@ -95,7 +96,8 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) { } System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) - : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} + : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()}, + adsp_rendered_event{adsp_rendered_event_} {} Result System::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, @@ -443,7 +445,7 @@ void System::Stop() { Result System::Update(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) { std::scoped_lock l{lock}; - const auto start_time{core.CoreTiming().GetClockTicks()}; + const auto start_time{core.CoreTiming().GetGlobalTimeNs().count()}; std::memset(output.data(), 0, output.size()); InfoUpdater info_updater(input, output, process_handle, behavior); @@ -535,7 +537,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std: adsp_rendered_event->Clear(); num_times_updated++; - const auto end_time{core.CoreTiming().GetClockTicks()}; + const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()}; ticks_spent_updating += end_time - start_time; return ResultSuccess; @@ -583,7 +585,7 @@ void System::SendCommandToDsp() { if (initialized) { if (active) { terminate_event.Reset(); - const auto remaining_command_count{adsp.GetRemainCommandCount(session_id)}; + const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)}; u64 command_size{0}; if (remaining_command_count) { @@ -607,26 +609,18 @@ void System::SendCommandToDsp() { time_limit_percent = 70.0f; } - ADSP::CommandBuffer command_buffer{ - .buffer{translated_addr}, - .size{command_size}, - .time_limit{ - static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 * - (static_cast<f32>(render_time_limit_percent) / 100.0f))}, - .remaining_command_count{remaining_command_count}, - .reset_buffers{reset_command_buffers}, - .applet_resource_user_id{applet_resource_user_id}, - .render_time_taken{adsp.GetRenderTimeTaken(session_id)}, - }; - - adsp.SendCommandBuffer(session_id, command_buffer); + auto time_limit{ + static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 * + (static_cast<f32>(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) { adsp_rendered_event->Signal(); } } else { - adsp.ClearRemainCount(session_id); + audio_renderer.ClearRemainCommandCount(session_id); terminate_event.Set(); } } @@ -635,7 +629,7 @@ void System::SendCommandToDsp() { u64 System::GenerateCommand(std::span<u8> in_command_buffer, [[maybe_unused]] u64 command_buffer_size_) { PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); - const auto start_time{core.CoreTiming().GetClockTicks()}; + const auto start_time{core.CoreTiming().GetGlobalTimeNs().count()}; auto command_list_header{reinterpret_cast<CommandListHeader*>(in_command_buffer.data())}; @@ -732,10 +726,10 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer, effect_context.UpdateStateByDspShared(); } - const auto end_time{core.CoreTiming().GetClockTicks()}; + const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()}; total_ticks_elapsed += end_time - start_time; num_command_lists_generated++; - render_start_tick = adsp.GetRenderingStartTick(session_id); + render_start_tick = audio_renderer.GetRenderingStartTick(session_id); frames_elapsed++; return command_buffer.size; @@ -819,4 +813,4 @@ u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time return voices_dropped; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h index e328783b6..8a8341710 100644 --- a/src/audio_core/renderer/system.h +++ b/src/audio_core/renderer/system.h @@ -34,12 +34,16 @@ class KTransferMemory; namespace AudioCore { struct AudioRendererParameterInternal; - -namespace AudioRenderer { -class CommandBuffer; namespace ADSP { class ADSP; +namespace AudioRenderer { +class AudioRenderer; } +} // namespace ADSP + +namespace Renderer { +using namespace ::AudioCore::ADSP; +class CommandBuffer; /** * Audio Renderer System, the main worker for audio rendering. @@ -213,8 +217,8 @@ public: private: /// Core system Core::System& core; - /// Reference to the ADSP for communication - ADSP::ADSP& adsp; + /// Reference to the ADSP's AudioRenderer for communication + ::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer; /// Is this system initialized? bool initialized{}; /// Is this system currently active? @@ -319,5 +323,5 @@ private: f32 drop_voice_param{1.0f}; }; -} // namespace AudioRenderer +} // namespace Renderer } // namespace AudioCore diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp index 300ecdbf1..a0b8ef29e 100644 --- a/src/audio_core/renderer/system_manager.cpp +++ b/src/audio_core/renderer/system_manager.cpp @@ -3,8 +3,8 @@ #include <chrono> +#include "audio_core/adsp/adsp.h" #include "audio_core/audio_core.h" -#include "audio_core/renderer/adsp/adsp.h" #include "audio_core/renderer/system_manager.h" #include "common/microprofile.h" #include "common/thread.h" @@ -14,24 +14,21 @@ MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager", MP_RGB(60, 19, 97)); -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { SystemManager::SystemManager(Core::System& core_) - : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()} {} + : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {} SystemManager::~SystemManager() { Stop(); } -bool SystemManager::InitializeUnsafe() { +void SystemManager::InitializeUnsafe() { if (!active) { - if (adsp.Start()) { - active = true; - thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); }); - } + active = true; + audio_renderer.Start(); + thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); }); } - - return adsp.GetState() == ADSP::State::Started; } void SystemManager::Stop() { @@ -41,7 +38,7 @@ void SystemManager::Stop() { active = false; thread.request_stop(); thread.join(); - adsp.Stop(); + audio_renderer.Stop(); } bool SystemManager::Add(System& system_) { @@ -55,10 +52,7 @@ bool SystemManager::Add(System& system_) { { std::scoped_lock l{mutex1}; if (systems.empty()) { - if (!InitializeUnsafe()) { - LOG_ERROR(Service_Audio, "Failed to start the AudioRenderer SystemManager"); - return false; - } + InitializeUnsafe(); } } @@ -100,9 +94,9 @@ void SystemManager::ThreadFunc(std::stop_token stop_token) { } } - adsp.Signal(); - adsp.Wait(); + audio_renderer.Signal(); + audio_renderer.Wait(); } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/system_manager.h b/src/audio_core/renderer/system_manager.h index 9681fd121..62e8e5f15 100644 --- a/src/audio_core/renderer/system_manager.h +++ b/src/audio_core/renderer/system_manager.h @@ -18,11 +18,14 @@ struct EventType; class System; } // namespace Core -namespace AudioCore::AudioRenderer { -namespace ADSP { +namespace AudioCore::ADSP { class ADSP; -class AudioRenderer_Mailbox; -} // namespace ADSP +namespace AudioRenderer { +class AudioRenderer; +} // namespace AudioRenderer +} // namespace AudioCore::ADSP + +namespace AudioCore::Renderer { /** * Manages all audio renderers, responsible for triggering command list generation and signalling @@ -38,7 +41,7 @@ public: * * @return True if successfully initialized, otherwise false. */ - bool InitializeUnsafe(); + void InitializeUnsafe(); /** * Stop the system manager. @@ -80,10 +83,8 @@ private: std::mutex mutex2{}; /// Is the system manager thread active? std::atomic<bool> active{}; - /// Reference to the ADSP for communication - ADSP::ADSP& adsp; - /// AudioRenderer mailbox for communication - ADSP::AudioRenderer_Mailbox* mailbox{}; + /// Reference to the ADSP's AudioRenderer for communication + ::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/upsampler/upsampler_info.h b/src/audio_core/renderer/upsampler/upsampler_info.h index a43c15af3..85c87f137 100644 --- a/src/audio_core/renderer/upsampler/upsampler_info.h +++ b/src/audio_core/renderer/upsampler/upsampler_info.h @@ -9,7 +9,7 @@ #include "audio_core/renderer/upsampler/upsampler_state.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class UpsamplerManager; /** @@ -32,4 +32,4 @@ struct UpsamplerInfo { std::array<s16, MaxChannels> inputs{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/upsampler/upsampler_manager.cpp b/src/audio_core/renderer/upsampler/upsampler_manager.cpp index 4c76a5066..ef740f6c9 100644 --- a/src/audio_core/renderer/upsampler/upsampler_manager.cpp +++ b/src/audio_core/renderer/upsampler/upsampler_manager.cpp @@ -3,7 +3,7 @@ #include "audio_core/renderer/upsampler/upsampler_manager.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { UpsamplerManager::UpsamplerManager(const u32 count_, std::span<UpsamplerInfo> infos_, std::span<s32> workbuffer_) @@ -41,4 +41,4 @@ void UpsamplerManager::Free(UpsamplerInfo* info) { info->enabled = false; } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/upsampler/upsampler_manager.h b/src/audio_core/renderer/upsampler/upsampler_manager.h index 83c697c0c..263e5718b 100644 --- a/src/audio_core/renderer/upsampler/upsampler_manager.h +++ b/src/audio_core/renderer/upsampler/upsampler_manager.h @@ -9,7 +9,7 @@ #include "audio_core/renderer/upsampler/upsampler_info.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Manages and has utility functions for upsampler infos. */ @@ -42,4 +42,4 @@ private: std::mutex lock{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/upsampler/upsampler_state.h b/src/audio_core/renderer/upsampler/upsampler_state.h index 28cebe200..dc7b31d42 100644 --- a/src/audio_core/renderer/upsampler/upsampler_state.h +++ b/src/audio_core/renderer/upsampler/upsampler_state.h @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Upsampling state used by the AudioRenderer across calls. */ @@ -37,4 +37,4 @@ struct UpsamplerState { u8 sample_index; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/voice/voice_channel_resource.h b/src/audio_core/renderer/voice/voice_channel_resource.h index 26ab4ccce..4f19c2fcc 100644 --- a/src/audio_core/renderer/voice/voice_channel_resource.h +++ b/src/audio_core/renderer/voice/voice_channel_resource.h @@ -8,7 +8,7 @@ #include "audio_core/common/common.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Represents one channel for mixing a voice. */ @@ -35,4 +35,4 @@ public: bool in_use{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp index 16a3e839d..c3644e38b 100644 --- a/src/audio_core/renderer/voice/voice_context.cpp +++ b/src/audio_core/renderer/voice/voice_context.cpp @@ -6,7 +6,7 @@ #include "audio_core/renderer/voice/voice_context.h" #include "common/polyfill_ranges.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { VoiceState& VoiceContext::GetDspSharedState(const u32 index) { if (index >= dsp_states.size()) { @@ -84,4 +84,4 @@ void VoiceContext::UpdateStateByDspShared() { std::memcpy(cpu_states.data(), dsp_states.data(), voice_count * sizeof(VoiceState)); } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/voice/voice_context.h b/src/audio_core/renderer/voice/voice_context.h index 43b677154..138ab2773 100644 --- a/src/audio_core/renderer/voice/voice_context.h +++ b/src/audio_core/renderer/voice/voice_context.h @@ -10,7 +10,7 @@ #include "audio_core/renderer/voice/voice_state.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Contains all voices, with utility functions for managing them. */ @@ -123,4 +123,4 @@ private: u32 active_count{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/voice/voice_info.cpp b/src/audio_core/renderer/voice/voice_info.cpp index c0bfb23fc..6239cfab7 100644 --- a/src/audio_core/renderer/voice/voice_info.cpp +++ b/src/audio_core/renderer/voice/voice_info.cpp @@ -6,7 +6,7 @@ #include "audio_core/renderer/voice/voice_info.h" #include "audio_core/renderer/voice/voice_state.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { VoiceInfo::VoiceInfo() { Initialize(); @@ -405,4 +405,4 @@ void VoiceInfo::ResetResources(VoiceContext& voice_context) const { } } -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/voice/voice_info.h b/src/audio_core/renderer/voice/voice_info.h index 3c5d3e04f..14a687dcb 100644 --- a/src/audio_core/renderer/voice/voice_info.h +++ b/src/audio_core/renderer/voice/voice_info.h @@ -12,7 +12,7 @@ #include "audio_core/renderer/memory/address_info.h" #include "common/common_types.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { class PoolMapper; class VoiceContext; struct VoiceState; @@ -377,4 +377,4 @@ public: u8 flush_buffer_count{}; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/voice/voice_state.h b/src/audio_core/renderer/voice/voice_state.h index ce947233f..c7aee167b 100644 --- a/src/audio_core/renderer/voice/voice_state.h +++ b/src/audio_core/renderer/voice/voice_state.h @@ -9,7 +9,7 @@ #include "common/common_types.h" #include "common/fixed_point.h" -namespace AudioCore::AudioRenderer { +namespace AudioCore::Renderer { /** * Holds a state for a voice. One is kept host-side, and one is used by the AudioRenderer, * host-side is updated on the next iteration. @@ -67,4 +67,4 @@ struct VoiceState { s32 loop_count; }; -} // namespace AudioCore::AudioRenderer +} // namespace AudioCore::Renderer diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bf97d9ba2..34877b461 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -151,6 +151,10 @@ add_library(common STATIC zstd_compression.h ) +if (YUZU_ENABLE_PORTABLE) + add_compile_definitions(YUZU_ENABLE_PORTABLE) +endif() + if (WIN32) target_sources(common PRIVATE windows/timer_resolution.cpp @@ -191,8 +195,6 @@ if (MSVC) _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING ) target_compile_options(common PRIVATE - /W4 - /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index d71cfacc6..dce219fcf 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -88,8 +88,9 @@ public: fs::path yuzu_path_config; #ifdef _WIN32 +#ifdef YUZU_ENABLE_PORTABLE yuzu_path = GetExeDirectory() / PORTABLE_DIR; - +#endif if (!IsDir(yuzu_path)) { yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR; } @@ -101,8 +102,9 @@ public: yuzu_path_cache = yuzu_path / CACHE_DIR; yuzu_path_config = yuzu_path / CONFIG_DIR; #else +#ifdef YUZU_ENABLE_PORTABLE yuzu_path = GetCurrentDir() / PORTABLE_DIR; - +#endif if (Exists(yuzu_path) && IsDir(yuzu_path)) { yuzu_path_cache = yuzu_path / CACHE_DIR; yuzu_path_config = yuzu_path / CONFIG_DIR; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 012648d69..c33910ade 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -584,13 +584,23 @@ add_library(core STATIC hle/service/lm/lm.h hle/service/mig/mig.cpp hle/service/mig/mig.h + hle/service/mii/types/char_info.cpp + hle/service/mii/types/char_info.h + hle/service/mii/types/core_data.cpp + hle/service/mii/types/core_data.h + hle/service/mii/types/raw_data.cpp + hle/service/mii/types/raw_data.h + hle/service/mii/types/store_data.cpp + hle/service/mii/types/store_data.h + hle/service/mii/types/ver3_store_data.cpp + hle/service/mii/types/ver3_store_data.h hle/service/mii/mii.cpp hle/service/mii/mii.h hle/service/mii/mii_manager.cpp hle/service/mii/mii_manager.h - hle/service/mii/raw_data.cpp - hle/service/mii/raw_data.h - hle/service/mii/types.h + hle/service/mii/mii_result.h + hle/service/mii/mii_types.h + hle/service/mii/mii_util.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h hle/service/mnpp/mnpp_app.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 2f67e60a9..2d6e61398 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -273,7 +273,8 @@ struct System::Impl { time_manager.Initialize(); is_powered_on = true; - exit_lock = false; + exit_locked = false; + exit_requested = false; microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); @@ -398,12 +399,14 @@ struct System::Impl { } is_powered_on = false; - exit_lock = false; + exit_locked = false; + exit_requested = false; if (gpu_core != nullptr) { gpu_core->NotifyShutdown(); } + Network::CancelPendingSocketOperations(); kernel.SuspendApplication(true); if (services) { services->KillNVNFlinger(); @@ -425,6 +428,7 @@ struct System::Impl { debugger.reset(); kernel.Shutdown(); memory.Reset(); + Network::RestartSocketOperations(); if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info{}; @@ -507,7 +511,8 @@ struct System::Impl { CpuManager cpu_manager; std::atomic_bool is_powered_on{}; - bool exit_lock = false; + bool exit_locked = false; + bool exit_requested = false; bool nvdec_active{}; @@ -559,6 +564,8 @@ struct System::Impl { std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> gpu_dirty_memory_write_manager{}; + + std::deque<std::vector<u8>> user_channel; }; System::System() : impl{std::make_unique<Impl>(*this)} {} @@ -943,12 +950,20 @@ const Service::Time::TimeManager& System::GetTimeManager() const { return impl->time_manager; } -void System::SetExitLock(bool locked) { - impl->exit_lock = locked; +void System::SetExitLocked(bool locked) { + impl->exit_locked = locked; +} + +bool System::GetExitLocked() const { + return impl->exit_locked; } -bool System::GetExitLock() const { - return impl->exit_lock; +void System::SetExitRequested(bool requested) { + impl->exit_requested = requested; +} + +bool System::GetExitRequested() const { + return impl->exit_requested; } void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { @@ -1025,6 +1040,10 @@ void System::ExecuteProgram(std::size_t program_index) { } } +std::deque<std::vector<u8>>& System::GetUserChannel() { + return impl->user_channel; +} + void System::RegisterExitCallback(ExitCallback&& callback) { impl->exit_callback = std::move(callback); } diff --git a/src/core/core.h b/src/core/core.h index c70ea1965..fba312125 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -4,6 +4,7 @@ #pragma once #include <cstddef> +#include <deque> #include <functional> #include <memory> #include <mutex> @@ -412,8 +413,11 @@ public: /// Gets an immutable reference to the Room Network. [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; - void SetExitLock(bool locked); - [[nodiscard]] bool GetExitLock() const; + void SetExitLocked(bool locked); + bool GetExitLocked() const; + + void SetExitRequested(bool requested); + bool GetExitRequested() const; void SetApplicationProcessBuildID(const CurrentBuildProcessID& id); [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const; @@ -456,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<std::vector<u8>>& GetUserChannel(); + /// Type used for the frontend to designate a callback for System to exit the application. using ExitCallback = std::function<void()>; diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 4ff2c50e5..e13c5cdc7 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -35,7 +35,6 @@ namespace Core::Crypto { namespace { constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; -constexpr u64 FULL_TICKET_SIZE = 0x400; using Common::AsArray; @@ -156,6 +155,10 @@ u64 GetSignatureTypePaddingSize(SignatureType type) { UNREACHABLE(); } +bool Ticket::IsValid() const { + return !std::holds_alternative<std::monostate>(data); +} + SignatureType Ticket::GetSignatureType() const { if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { return ticket->sig_type; @@ -210,6 +213,54 @@ Ticket Ticket::SynthesizeCommon(Key128 title_key, const std::array<u8, 16>& righ return Ticket{out}; } +Ticket Ticket::Read(const FileSys::VirtualFile& file) { + // Attempt to read up to the largest ticket size, and make sure we read at least a signature + // type. + std::array<u8, sizeof(RSA4096Ticket)> raw_data{}; + auto read_size = file->Read(raw_data.data(), raw_data.size(), 0); + if (read_size < sizeof(SignatureType)) { + LOG_WARNING(Crypto, "Attempted to read ticket file with invalid size {}.", read_size); + return Ticket{std::monostate()}; + } + return Read(std::span{raw_data}); +} + +Ticket Ticket::Read(std::span<const u8> raw_data) { + // Some tools read only 0x180 bytes of ticket data instead of 0x2C0, so + // just make sure we have at least the bare minimum of data to work with. + SignatureType sig_type; + if (raw_data.size() < sizeof(SignatureType)) { + LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid size {}.", + raw_data.size()); + return Ticket{std::monostate()}; + } + std::memcpy(&sig_type, raw_data.data(), sizeof(sig_type)); + + switch (sig_type) { + case SignatureType::RSA_4096_SHA1: + case SignatureType::RSA_4096_SHA256: { + RSA4096Ticket ticket{}; + std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); + return Ticket{ticket}; + } + case SignatureType::RSA_2048_SHA1: + case SignatureType::RSA_2048_SHA256: { + RSA2048Ticket ticket{}; + std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); + return Ticket{ticket}; + } + case SignatureType::ECDSA_SHA1: + case SignatureType::ECDSA_SHA256: { + ECDSATicket ticket{}; + std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); + return Ticket{ticket}; + } + default: + LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid type {}.", sig_type); + return Ticket{std::monostate()}; + } +} + Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { Key128 out{}; @@ -290,9 +341,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) { } } -RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { +void KeyManager::DeriveETicketRSAKey() { if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) { - return {}; + return; } const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); @@ -304,12 +355,12 @@ RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, extended_dec.data(), Op::Decrypt); - RSAKeyPair<2048> rsa_key{}; - std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); - std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); - std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); - - return rsa_key; + std::memcpy(eticket_rsa_keypair.decryption_key.data(), extended_dec.data(), + eticket_rsa_keypair.decryption_key.size()); + std::memcpy(eticket_rsa_keypair.modulus.data(), extended_dec.data() + 0x100, + eticket_rsa_keypair.modulus.size()); + std::memcpy(eticket_rsa_keypair.exponent.data(), extended_dec.data() + 0x200, + eticket_rsa_keypair.exponent.size()); } Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { @@ -447,10 +498,12 @@ std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) { for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && buffer[offset + 3] == 0x0) { - out.emplace_back(); - auto& next = out.back(); - std::memcpy(&next, buffer.data() + offset, sizeof(Ticket)); - offset += FULL_TICKET_SIZE; + // NOTE: Assumes ticket blob will only contain RSA-2048 tickets. + auto ticket = Ticket::Read(std::span{buffer.data() + offset, sizeof(RSA2048Ticket)}); + offset += sizeof(RSA2048Ticket); + if (ticket.IsValid()) { + out.push_back(ticket); + } } } @@ -503,25 +556,36 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { return offset; } -std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, - const RSAKeyPair<2048>& key) { +std::optional<Key128> KeyManager::ParseTicketTitleKey(const Ticket& ticket) { + if (!ticket.IsValid()) { + LOG_WARNING(Crypto, "Attempted to parse title key of invalid ticket."); + return std::nullopt; + } + + if (ticket.GetData().rights_id == Key128{}) { + LOG_WARNING(Crypto, "Attempted to parse title key of ticket with no rights ID."); + return std::nullopt; + } + const auto issuer = ticket.GetData().issuer; if (IsAllZeroArray(issuer)) { + LOG_WARNING(Crypto, "Attempted to parse title key of ticket with invalid issuer."); return std::nullopt; } + if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { - LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); + LOG_WARNING(Crypto, "Parsing ticket with non-standard certificate authority."); } - Key128 rights_id = ticket.GetData().rights_id; - - if (rights_id == Key128{}) { - return std::nullopt; + if (ticket.GetData().type == TitleKeyType::Common) { + return ticket.GetData().title_key_common; } - if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), - ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { - return std::make_pair(rights_id, ticket.GetData().title_key_common); + if (eticket_rsa_keypair == RSAKeyPair<2048>{}) { + LOG_WARNING( + Crypto, + "Skipping personalized ticket title key parsing due to missing ETicket RSA key-pair."); + return std::nullopt; } mbedtls_mpi D; // RSA Private Exponent @@ -534,9 +598,12 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, mbedtls_mpi_init(&S); mbedtls_mpi_init(&M); - mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); - mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); - mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100); + const auto& title_key_block = ticket.GetData().title_key_block; + mbedtls_mpi_read_binary(&D, eticket_rsa_keypair.decryption_key.data(), + eticket_rsa_keypair.decryption_key.size()); + mbedtls_mpi_read_binary(&N, eticket_rsa_keypair.modulus.data(), + eticket_rsa_keypair.modulus.size()); + mbedtls_mpi_read_binary(&S, title_key_block.data(), title_key_block.size()); mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); @@ -564,8 +631,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, Key128 key_temp{}; std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); - - return std::make_pair(rights_id, key_temp); + return key_temp; } KeyManager::KeyManager() { @@ -669,6 +735,14 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti 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]); + } else if (out[0].compare(0, 19, "eticket_rsa_keypair") == 0) { + const auto key_data = Common::HexStringToArray<528>(out[1]); + std::memcpy(eticket_rsa_keypair.decryption_key.data(), key_data.data(), + eticket_rsa_keypair.decryption_key.size()); + std::memcpy(eticket_rsa_keypair.modulus.data(), key_data.data() + 0x100, + eticket_rsa_keypair.modulus.size()); + std::memcpy(eticket_rsa_keypair.exponent.data(), key_data.data() + 0x200, + eticket_rsa_keypair.exponent.size()); } else { for (const auto& kv : KEYS_VARIABLE_LENGTH) { if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) { @@ -1110,56 +1184,38 @@ void KeyManager::DeriveETicket(PartitionDataManager& data, eticket_extended_kek = data.GetETicketExtendedKek(); WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); + DeriveETicketRSAKey(); PopulateTickets(); } void KeyManager::PopulateTickets() { - const auto rsa_key = GetETicketRSAKey(); - - if (rsa_key == RSAKeyPair<2048>{}) { + if (ticket_databases_loaded) { return; } + ticket_databases_loaded = true; - if (!common_tickets.empty() && !personal_tickets.empty()) { - return; - } + std::vector<Ticket> tickets; const auto system_save_e1_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1"; - - const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; + if (Common::FS::Exists(system_save_e1_path)) { + const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + const auto blob1 = GetTicketblob(save_e1); + tickets.insert(tickets.end(), blob1.begin(), blob1.end()); + } const auto system_save_e2_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2"; + if (Common::FS::Exists(system_save_e2_path)) { + const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + const auto blob2 = GetTicketblob(save_e2); + tickets.insert(tickets.end(), blob2.begin(), blob2.end()); + } - const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; - - const auto blob2 = GetTicketblob(save_e2); - auto res = GetTicketblob(save_e1); - - const auto idx = res.size(); - res.insert(res.end(), blob2.begin(), blob2.end()); - - for (std::size_t i = 0; i < res.size(); ++i) { - const auto common = i < idx; - const auto pair = ParseTicket(res[i], rsa_key); - if (!pair) { - continue; - } - - const auto& [rid, key] = *pair; - u128 rights_id; - std::memcpy(rights_id.data(), rid.data(), rid.size()); - - if (common) { - common_tickets[rights_id] = res[i]; - } else { - personal_tickets[rights_id] = res[i]; - } - - SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + for (const auto& ticket : tickets) { + AddTicket(ticket); } } @@ -1291,41 +1347,33 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const { return personal_tickets; } -bool KeyManager::AddTicketCommon(Ticket raw) { - const auto rsa_key = GetETicketRSAKey(); - if (rsa_key == RSAKeyPair<2048>{}) { - return false; - } - - const auto pair = ParseTicket(raw, rsa_key); - if (!pair) { +bool KeyManager::AddTicket(const Ticket& ticket) { + if (!ticket.IsValid()) { + LOG_WARNING(Crypto, "Attempted to add invalid ticket."); return false; } - const auto& [rid, key] = *pair; + const auto& rid = ticket.GetData().rights_id; u128 rights_id; std::memcpy(rights_id.data(), rid.data(), rid.size()); - common_tickets[rights_id] = raw; - SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); - return true; -} + if (ticket.GetData().type == Core::Crypto::TitleKeyType::Common) { + common_tickets[rights_id] = ticket; + } else { + personal_tickets[rights_id] = ticket; + } -bool KeyManager::AddTicketPersonalized(Ticket raw) { - const auto rsa_key = GetETicketRSAKey(); - if (rsa_key == RSAKeyPair<2048>{}) { - return false; + if (HasKey(S128KeyType::Titlekey, rights_id[1], rights_id[0])) { + LOG_DEBUG(Crypto, + "Skipping parsing title key from ticket for known rights ID {:016X}{:016X}.", + rights_id[1], rights_id[0]); + return true; } - const auto pair = ParseTicket(raw, rsa_key); - if (!pair) { + const auto key = ParseTicketTitleKey(ticket); + if (!key) { return false; } - - const auto& [rid, key] = *pair; - u128 rights_id; - std::memcpy(rights_id.data(), rid.data(), rid.size()); - common_tickets[rights_id] = raw; - SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + SetKey(S128KeyType::Titlekey, key.value(), rights_id[1], rights_id[0]); return true; } } // namespace Core::Crypto diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 8c864503b..2250eccec 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -7,6 +7,7 @@ #include <filesystem> #include <map> #include <optional> +#include <span> #include <string> #include <variant> @@ -29,8 +30,6 @@ enum class ResultStatus : u16; namespace Core::Crypto { -constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180; - using Key128 = std::array<u8, 0x10>; using Key256 = std::array<u8, 0x20>; using SHA256Hash = std::array<u8, 0x20>; @@ -82,6 +81,7 @@ struct RSA4096Ticket { INSERT_PADDING_BYTES(0x3C); TicketData data; }; +static_assert(sizeof(RSA4096Ticket) == 0x500, "RSA4096Ticket has incorrect size."); struct RSA2048Ticket { SignatureType sig_type; @@ -89,6 +89,7 @@ struct RSA2048Ticket { INSERT_PADDING_BYTES(0x3C); TicketData data; }; +static_assert(sizeof(RSA2048Ticket) == 0x400, "RSA2048Ticket has incorrect size."); struct ECDSATicket { SignatureType sig_type; @@ -96,16 +97,41 @@ struct ECDSATicket { INSERT_PADDING_BYTES(0x40); TicketData data; }; +static_assert(sizeof(ECDSATicket) == 0x340, "ECDSATicket has incorrect size."); struct Ticket { - std::variant<RSA4096Ticket, RSA2048Ticket, ECDSATicket> data; - - SignatureType GetSignatureType() const; - TicketData& GetData(); - const TicketData& GetData() const; - u64 GetSize() const; - + std::variant<std::monostate, RSA4096Ticket, RSA2048Ticket, ECDSATicket> data; + + [[nodiscard]] bool IsValid() const; + [[nodiscard]] SignatureType GetSignatureType() const; + [[nodiscard]] TicketData& GetData(); + [[nodiscard]] const TicketData& GetData() const; + [[nodiscard]] u64 GetSize() const; + + /** + * Synthesizes a common ticket given a title key and rights ID. + * + * @param title_key Title key to store in the ticket. + * @param rights_id Rights ID the ticket is for. + * @return The synthesized common ticket. + */ static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id); + + /** + * Reads a ticket from a file. + * + * @param file File to read the ticket from. + * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false. + */ + static Ticket Read(const FileSys::VirtualFile& file); + + /** + * Reads a ticket from a memory buffer. + * + * @param raw_data Buffer to read the ticket from. + * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false. + */ + static Ticket Read(std::span<const u8> raw_data); }; static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); @@ -264,8 +290,7 @@ public: const std::map<u128, Ticket>& GetCommonTickets() const; const std::map<u128, Ticket>& GetPersonalizedTickets() const; - bool AddTicketCommon(Ticket raw); - bool AddTicketPersonalized(Ticket raw); + bool AddTicket(const Ticket& ticket); void ReloadKeys(); bool AreKeysLoaded() const; @@ -279,10 +304,12 @@ private: // Map from rights ID to ticket std::map<u128, Ticket> common_tickets; std::map<u128, Ticket> personal_tickets; + bool ticket_databases_loaded = false; std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; std::array<u8, 576> eticket_extended_kek{}; + RSAKeyPair<2048> eticket_rsa_keypair{}; bool dev_mode; void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); @@ -293,10 +320,13 @@ private: void DeriveGeneralPurposeKeys(std::size_t crypto_revision); - RSAKeyPair<2048> GetETicketRSAKey() const; + void DeriveETicketRSAKey(); void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); + + /// Parses the title key section of a ticket. + std::optional<Key128> ParseTicketTitleKey(const Ticket& ticket); }; Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); @@ -311,9 +341,4 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save); -// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority -// (offset 0x140-0x144 is zero) -std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, - const RSAKeyPair<2048>& eticket_extended_key); - } // namespace Core::Crypto diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 44e6852fe..7d2f0abb8 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -22,6 +22,10 @@ namespace FileSys { +static u8 MasterKeyIdForKeyGeneration(u8 key_generation) { + return std::max<u8>(key_generation, 1) - 1; +} + NCA::NCA(VirtualFile file_, const NCA* base_nca) : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { if (file == nullptr) { @@ -41,12 +45,17 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) return; } + // Ensure we have the proper key area keys to continue. + const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration()); + if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) { + status = Loader::ResultStatus::ErrorMissingKeyAreaKey; + return; + } + RightsId rights_id{}; reader->GetRightsId(rights_id.data(), rights_id.size()); if (rights_id != RightsId{}) { // External decryption key required; provide it here. - const auto key_generation = std::max<s32>(reader->GetKeyGeneration(), 1) - 1; - u128 rights_id_u128; std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id)); @@ -57,12 +66,12 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) return; } - if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, key_generation)) { + if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { status = Loader::ResultStatus::ErrorMissingTitlekek; return; } - auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, key_generation); + auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id); Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB); cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt); diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 52c78020c..f4a774675 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -45,6 +45,10 @@ CNMT::CNMT(CNMTHeader header_, OptionalHeader opt_header_, CNMT::~CNMT() = default; +const CNMTHeader& CNMT::GetHeader() const { + return header; +} + u64 CNMT::GetTitleID() const { return header.title_id; } diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index c59ece010..68e463b5f 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -89,6 +89,7 @@ public: std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_); ~CNMT(); + const CNMTHeader& GetHeader() const; u64 GetTitleID() const; u32 GetTitleVersion() const; TitleType GetType() const; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index f70adab82..e33b00d89 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -9,6 +9,7 @@ #include "common/fs/path_util.h" #include "common/hex_util.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" #include "core/file_sys/common_funcs.h" @@ -625,7 +626,7 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex nca->GetTitleId() != title_id) { // Create fake cnmt for patch to multiprogram application const auto sub_nca_result = - InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy); + InstallEntry(*nca, cnmt.GetHeader(), record, overwrite_if_exists, copy); if (sub_nca_result != InstallResult::Success) { return sub_nca_result; } @@ -672,6 +673,31 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); } +InstallResult RegisteredCache::InstallEntry(const NCA& nca, const CNMTHeader& base_header, + const ContentRecord& base_record, + bool overwrite_if_exists, const VfsCopyFunction& copy) { + const CNMTHeader header{ + .title_id = nca.GetTitleId(), + .title_version = base_header.title_version, + .type = base_header.type, + .reserved = {}, + .table_offset = 0x10, + .number_content_entries = 1, + .number_meta_entries = 0, + .attributes = 0, + .reserved2 = {}, + .is_committed = 0, + .required_download_system_version = 0, + .reserved3 = {}, + }; + const OptionalHeader opt_header{0, 0}; + const CNMT new_cnmt(header, opt_header, {base_record}, {}); + if (!RawInstallYuzuMeta(new_cnmt)) { + return InstallResult::ErrorMetaFailed; + } + return RawInstallNCA(nca, copy, overwrite_if_exists, base_record.nca_id); +} + bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { bool removed_data = false; diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index bd7f53eaf..64815a845 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -24,6 +24,7 @@ enum class NCAContentType : u8; enum class TitleType : u8; struct ContentRecord; +struct CNMTHeader; struct MetaRecord; class RegisteredCache; @@ -169,6 +170,10 @@ public: InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); + InstallResult InstallEntry(const NCA& nca, const CNMTHeader& base_header, + const ContentRecord& base_record, bool overwrite_if_exists = false, + const VfsCopyFunction& copy = &VfsRawCopy); + // Removes an existing entry based on title id bool RemoveExistingEntry(u64 title_id) const; diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index e1e89ce2d..68e8ec22f 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -164,24 +164,6 @@ VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType titl return nullptr; } -std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const { - if (extracted) - LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); - std::vector<Core::Crypto::Key128> out; - for (const auto& ticket_file : ticket_files) { - if (ticket_file == nullptr || - ticket_file->GetSize() < - Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { - continue; - } - - out.emplace_back(); - ticket_file->Read(out.back().data(), out.back().size(), - Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); - } - return out; -} - std::vector<VirtualFile> NSP::GetFiles() const { return pfs->GetFiles(); } @@ -208,22 +190,11 @@ void NSP::SetTicketKeys(const std::vector<VirtualFile>& files) { continue; } - if (ticket_file->GetSize() < - Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { + auto ticket = Core::Crypto::Ticket::Read(ticket_file); + if (!keys.AddTicket(ticket)) { + LOG_WARNING(Common_Filesystem, "Could not load NSP ticket {}", ticket_file->GetName()); continue; } - - Core::Crypto::Key128 key{}; - ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); - - // We get the name without the extension in order to create the rights ID. - std::string name_only(ticket_file->GetName()); - name_only.erase(name_only.size() - 4); - - const auto rights_id_raw = Common::HexStringToArray<16>(name_only); - u128 rights_id; - std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); - keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); } } diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 27f97c725..915bffca9 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -53,7 +53,6 @@ public: TitleType title_type = TitleType::Application) const; VirtualFile GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type = TitleType::Application) const; - std::vector<Core::Crypto::Key128> GetTitlekey() const; std::vector<VirtualFile> GetFiles() const override; diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp index 90e4e8fb0..e7da7a21d 100644 --- a/src/core/hle/kernel/k_capabilities.cpp +++ b/src/core/hle/kernel/k_capabilities.cpp @@ -156,7 +156,6 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) { const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; const size_t num_pages = 1; const size_t size = num_pages * PageSize; - R_UNLESS(num_pages != 0, ResultInvalidSize); R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress); R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index da33f0e44..f9c4f9678 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, }; @@ -341,7 +341,7 @@ void ISelfController::Exit(HLERequestContext& ctx) { void ISelfController::LockExit(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); - system.SetExitLock(true); + system.SetExitLocked(true); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -350,10 +350,14 @@ void ISelfController::LockExit(HLERequestContext& ctx) { void ISelfController::UnlockExit(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); - system.SetExitLock(false); + system.SetExitLocked(false); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); + + if (system.GetExitRequested()) { + system.Exit(); + } } void ISelfController::EnterFatalSection(HLERequestContext& ctx) { @@ -1514,27 +1518,26 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto kind = rp.PopEnum<LaunchParameterKind>(); - LOG_DEBUG(Service_AM, "called, kind={:08X}", kind); - - if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) { - const auto backend = BCAT::CreateBackendFromSettings(system, [this](u64 tid) { - return system.GetFileSystemController().GetBCATDirectory(tid); - }); - const auto build_id_full = system.GetApplicationProcessBuildID(); - u64 build_id{}; - std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); - - auto data = - backend->GetLaunchParameter({system.GetApplicationProcessProgramID(), build_id}); - if (data.has_value()) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>(system, std::move(*data)); - launch_popped_application_specific = true; + LOG_INFO(Service_AM, "called, kind={:08X}", kind); + + if (kind == LaunchParameterKind::UserChannel) { + auto channel = system.GetUserChannel(); + if (channel.empty()) { + LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(AM::ResultNoDataInChannel); return; } + + auto data = channel.back(); + channel.pop_back(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IStorage>(system, std::move(data)); } else if (kind == LaunchParameterKind::AccountPreselectedUser && !launch_popped_account_preselect) { + // TODO: Verify this is hw-accurate LaunchParameterAccountPreselectedUser params{}; params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC; @@ -1546,7 +1549,6 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { params.current_user = *uuid; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); @@ -1554,12 +1556,11 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { rb.PushIpcInterface<IStorage>(system, std::move(buffer)); launch_popped_account_preselect = true; - return; + } else { + LOG_ERROR(Service_AM, "Unknown launch parameter kind."); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(AM::ResultNoDataInChannel); } - - LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); } void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) { @@ -1851,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<IStorage>().lock(); + if (storage) { + system.GetUserChannel().push_back(storage->GetData()); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index d68998f04..f75a665b2 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; 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 d1f652c09..350a90818 100644 --- a/src/core/hle/service/am/applets/applet_mii_edit.cpp +++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp @@ -85,15 +85,18 @@ void MiiEdit::Execute() { break; case MiiEditAppletMode::CreateMii: case MiiEditAppletMode::EditMii: { - Service::Mii::MiiManager mii_manager; + Mii::CharInfo char_info{}; + Mii::StoreData store_data{}; + store_data.BuildBase(Mii::Gender::Male); + char_info.SetFromStoreData(store_data); - const MiiEditCharInfo char_info{ + const MiiEditCharInfo edit_char_info{ .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii ? applet_input_v4.char_info.mii_info - : mii_manager.BuildDefault(0)}, + : char_info}, }; - MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info); + MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info); break; } default: diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h index 4705d019f..f3d764073 100644 --- a/src/core/hle/service/am/applets/applet_mii_edit_types.h +++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h @@ -7,7 +7,8 @@ #include "common/common_funcs.h" #include "common/common_types.h" -#include "core/hle/service/mii/types.h" +#include "common/uuid.h" +#include "core/hle/service/mii/types/char_info.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 526a39130..56fee4591 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -220,7 +220,7 @@ AudInU::AudInU(Core::System& system_) AudInU::~AudInU() = default; void AudInU::ListAudioIns(HLERequestContext& ctx) { - using namespace AudioCore::AudioRenderer; + using namespace AudioCore::Renderer; LOG_DEBUG(Service_Audio, "called"); @@ -240,7 +240,7 @@ void AudInU::ListAudioIns(HLERequestContext& ctx) { } void AudInU::ListAudioInsAutoFiltered(HLERequestContext& ctx) { - using namespace AudioCore::AudioRenderer; + using namespace AudioCore::Renderer; LOG_DEBUG(Service_Audio, "called"); diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 23f84a29f..ca683d72c 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -228,7 +228,7 @@ AudOutU::AudOutU(Core::System& system_) AudOutU::~AudOutU() = default; void AudOutU::ListAudioOuts(HLERequestContext& ctx) { - using namespace AudioCore::AudioRenderer; + using namespace AudioCore::Renderer; std::scoped_lock l{impl->mutex}; diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index b723b65c8..2f09cade5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -26,7 +26,7 @@ #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" -using namespace AudioCore::AudioRenderer; +using namespace AudioCore::Renderer; namespace Service::Audio { diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index d8e9c8719..3d7993a16 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -28,7 +28,7 @@ private: void GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx); KernelHelpers::ServiceContext service_context; - std::unique_ptr<AudioCore::AudioRenderer::Manager> impl; + std::unique_ptr<AudioCore::Renderer::Manager> impl; u32 num_audio_devices{0}; }; diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 446f46b3c..9eaae4c4b 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -122,20 +122,18 @@ private: } void ImportTicket(HLERequestContext& ctx) { - const auto ticket = ctx.ReadBuffer(); + const auto raw_ticket = ctx.ReadBuffer(); [[maybe_unused]] const auto cert = ctx.ReadBuffer(1); - if (ticket.size() < sizeof(Core::Crypto::Ticket)) { + if (raw_ticket.size() < sizeof(Core::Crypto::Ticket)) { LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_ARGUMENT); return; } - Core::Crypto::Ticket raw{}; - std::memcpy(&raw, ticket.data(), sizeof(Core::Crypto::Ticket)); - - if (!keys.AddTicketPersonalized(raw)) { + Core::Crypto::Ticket ticket = Core::Crypto::Ticket::Read(raw_ticket); + if (!keys.AddTicket(ticket)) { LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_ARGUMENT); diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 65c11a2f3..3b83c5ed7 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -7,17 +7,16 @@ #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/mii/mii.h" #include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/mii/mii_result.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" namespace Service::Mii { -constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; - class IDatabaseService final : public ServiceFramework<IDatabaseService> { public: - explicit IDatabaseService(Core::System& system_) - : ServiceFramework{system_, "IDatabaseService"} { + explicit IDatabaseService(Core::System& system_, bool is_system_) + : ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} { // clang-format off static const FunctionInfo functions[] = { {0, &IDatabaseService::IsUpdated, "IsUpdated"}, @@ -54,34 +53,27 @@ public: } private: - template <typename T> - std::vector<u8> SerializeArray(const std::vector<T>& values) { - std::vector<u8> out(values.size() * sizeof(T)); - std::size_t offset{}; - for (const auto& value : values) { - std::memcpy(out.data() + offset, &value, sizeof(T)); - offset += sizeof(T); - } - return out; - } - void IsUpdated(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto source_flag{rp.PopRaw<SourceFlag>()}; LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); + const bool is_updated = manager.IsUpdated(metadata, source_flag); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter)); + rb.Push<u8>(is_updated); } void IsFullDatabase(HLERequestContext& ctx) { LOG_DEBUG(Service_Mii, "called"); + const bool is_full_database = manager.IsFullDatabase(); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(manager.IsFullDatabase()); + rb.Push<u8>(is_full_database); } void GetCount(HLERequestContext& ctx) { @@ -90,57 +82,63 @@ private: LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); + const u32 mii_count = manager.GetCount(metadata, source_flag); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(manager.GetCount(source_flag)); + rb.Push(mii_count); } void Get(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto source_flag{rp.PopRaw<SourceFlag>()}; + const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()}; - LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); + LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size); + + u32 mii_count{}; + std::vector<CharInfoElement> char_info_elements(output_size); + Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag); - const auto default_miis{manager.GetDefault(source_flag)}; - if (default_miis.size() > 0) { - ctx.WriteBuffer(SerializeArray(default_miis)); + if (mii_count != 0) { + ctx.WriteBuffer(char_info_elements); } IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(default_miis.size())); + rb.Push(result); + rb.Push(mii_count); } void Get1(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto source_flag{rp.PopRaw<SourceFlag>()}; + const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()}; - LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); + LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size); - const auto default_miis{manager.GetDefault(source_flag)}; + u32 mii_count{}; + std::vector<CharInfo> char_info(output_size); + Result result = manager.Get(metadata, char_info, mii_count, source_flag); - std::vector<CharInfo> values; - for (const auto& element : default_miis) { - values.emplace_back(element.info); + if (mii_count != 0) { + ctx.WriteBuffer(char_info); } - ctx.WriteBuffer(SerializeArray(values)); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(default_miis.size())); + rb.Push(result); + rb.Push(mii_count); } void UpdateLatest(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto info{rp.PopRaw<CharInfo>()}; + const auto char_info{rp.PopRaw<CharInfo>()}; const auto source_flag{rp.PopRaw<SourceFlag>()}; LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); CharInfo new_char_info{}; - const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)}; - if (result != ResultSuccess) { + const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag); + if (result.IsFailure()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); return; @@ -153,7 +151,6 @@ private: void BuildRandom(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto age{rp.PopRaw<Age>()}; const auto gender{rp.PopRaw<Gender>()}; const auto race{rp.PopRaw<Race>()}; @@ -162,47 +159,48 @@ private: if (age > Age::All) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); - LOG_ERROR(Service_Mii, "invalid age={}", age); + rb.Push(ResultInvalidArgument); return; } if (gender > Gender::All) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); - LOG_ERROR(Service_Mii, "invalid gender={}", gender); + rb.Push(ResultInvalidArgument); return; } if (race > Race::All) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); - LOG_ERROR(Service_Mii, "invalid race={}", race); + rb.Push(ResultInvalidArgument); return; } + CharInfo char_info{}; + manager.BuildRandom(char_info, age, gender, race); + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race)); + rb.PushRaw<CharInfo>(char_info); } void BuildDefault(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto index{rp.Pop<u32>()}; - LOG_DEBUG(Service_Mii, "called with index={}", index); + LOG_INFO(Service_Mii, "called with index={}", index); if (index > 5) { - LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}", - index); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); + rb.Push(ResultInvalidArgument); return; } + CharInfo char_info{}; + manager.BuildDefault(char_info, index); + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<CharInfo>(manager.BuildDefault(index)); + rb.PushRaw<CharInfo>(char_info); } void GetIndex(HLERequestContext& ctx) { @@ -211,19 +209,21 @@ private: LOG_DEBUG(Service_Mii, "called"); - u32 index{}; + s32 index{}; + const auto result = manager.GetIndex(metadata, info, index); + IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(manager.GetIndex(info, index)); + rb.Push(result); rb.Push(index); } void SetInterfaceVersion(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - current_interface_version = rp.PopRaw<u32>(); + const auto interface_version{rp.PopRaw<u32>()}; - LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version); + LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version); - UNIMPLEMENTED_IF(current_interface_version != 1); + manager.SetInterfaceVersion(metadata, interface_version); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -231,30 +231,27 @@ private: void Convert(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; LOG_INFO(Service_Mii, "called"); + CharInfo char_info{}; + manager.ConvertV3ToCharInfo(char_info, mii_v3); + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3)); - } - - constexpr bool IsInterfaceVersionSupported(u32 interface_version) const { - return current_interface_version >= interface_version; + rb.PushRaw<CharInfo>(char_info); } - MiiManager manager; - - u32 current_interface_version{}; - u64 current_update_counter{}; + MiiManager manager{}; + DatabaseSessionMetadata metadata{}; + bool is_system{}; }; class MiiDBModule final : public ServiceFramework<MiiDBModule> { public: - explicit MiiDBModule(Core::System& system_, const char* name_) - : ServiceFramework{system_, name_} { + explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_) + : ServiceFramework{system_, name_}, is_system{is_system_} { // clang-format off static const FunctionInfo functions[] = { {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, @@ -268,10 +265,12 @@ private: void GetDatabaseService(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IDatabaseService>(system); + rb.PushIpcInterface<IDatabaseService>(system, is_system); LOG_DEBUG(Service_Mii, "called"); } + + bool is_system{}; }; class MiiImg final : public ServiceFramework<MiiImg> { @@ -303,8 +302,10 @@ public: void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - server_manager->RegisterNamedService("mii:e", std::make_shared<MiiDBModule>(system, "mii:e")); - server_manager->RegisterNamedService("mii:u", std::make_shared<MiiDBModule>(system, "mii:u")); + server_manager->RegisterNamedService("mii:e", + std::make_shared<MiiDBModule>(system, "mii:e", true)); + server_manager->RegisterNamedService("mii:u", + std::make_shared<MiiDBModule>(system, "mii:u", false)); server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 46125d473..292d63777 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -10,386 +10,24 @@ #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/mii/mii_manager.h" -#include "core/hle/service/mii/raw_data.h" +#include "core/hle/service/mii/mii_result.h" +#include "core/hle/service/mii/mii_util.h" +#include "core/hle/service/mii/types/core_data.h" +#include "core/hle/service/mii/types/raw_data.h" namespace Service::Mii { - -namespace { - -constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; - -constexpr std::size_t BaseMiiCount{2}; constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; -constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; -constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7}; -constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13}; -constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23}; -constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0}; -constexpr std::array<u8, 62> EyeRotateLookup{ - {0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, - 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, - 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}}; -constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, - 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06, - 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}}; - -template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize> -std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) { - std::array<T, DestArraySize> out{}; - std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize)); - return out; -} - -CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) { - MiiStoreBitFields bf; - std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); - - return { - .uuid = data.data.uuid, - .name = ResizeArray<char16_t, 10, 11>(data.data.name), - .font_region = static_cast<u8>(bf.font_region.Value()), - .favorite_color = static_cast<u8>(bf.favorite_color.Value()), - .gender = static_cast<u8>(bf.gender.Value()), - .height = static_cast<u8>(bf.height.Value()), - .build = static_cast<u8>(bf.build.Value()), - .type = static_cast<u8>(bf.type.Value()), - .region_move = static_cast<u8>(bf.region_move.Value()), - .faceline_type = static_cast<u8>(bf.faceline_type.Value()), - .faceline_color = static_cast<u8>(bf.faceline_color.Value()), - .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()), - .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()), - .hair_type = static_cast<u8>(bf.hair_type.Value()), - .hair_color = static_cast<u8>(bf.hair_color.Value()), - .hair_flip = static_cast<u8>(bf.hair_flip.Value()), - .eye_type = static_cast<u8>(bf.eye_type.Value()), - .eye_color = static_cast<u8>(bf.eye_color.Value()), - .eye_scale = static_cast<u8>(bf.eye_scale.Value()), - .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()), - .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()), - .eye_x = static_cast<u8>(bf.eye_x.Value()), - .eye_y = static_cast<u8>(bf.eye_y.Value()), - .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()), - .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()), - .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()), - .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()), - .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()), - .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()), - .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3), - .nose_type = static_cast<u8>(bf.nose_type.Value()), - .nose_scale = static_cast<u8>(bf.nose_scale.Value()), - .nose_y = static_cast<u8>(bf.nose_y.Value()), - .mouth_type = static_cast<u8>(bf.mouth_type.Value()), - .mouth_color = static_cast<u8>(bf.mouth_color.Value()), - .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()), - .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()), - .mouth_y = static_cast<u8>(bf.mouth_y.Value()), - .beard_color = static_cast<u8>(bf.beard_color.Value()), - .beard_type = static_cast<u8>(bf.beard_type.Value()), - .mustache_type = static_cast<u8>(bf.mustache_type.Value()), - .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()), - .mustache_y = static_cast<u8>(bf.mustache_y.Value()), - .glasses_type = static_cast<u8>(bf.glasses_type.Value()), - .glasses_color = static_cast<u8>(bf.glasses_color.Value()), - .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()), - .glasses_y = static_cast<u8>(bf.glasses_y.Value()), - .mole_type = static_cast<u8>(bf.mole_type.Value()), - .mole_scale = static_cast<u8>(bf.mole_scale.Value()), - .mole_x = static_cast<u8>(bf.mole_x.Value()), - .mole_y = static_cast<u8>(bf.mole_y.Value()), - .padding = 0, - }; -} - -u16 GenerateCrc16(const void* data, std::size_t size) { - s32 crc{}; - for (std::size_t i = 0; i < size; i++) { - crc ^= static_cast<const u8*>(data)[i] << 8; - for (std::size_t j = 0; j < 8; j++) { - crc <<= 1; - if ((crc & 0x10000) != 0) { - crc = (crc ^ 0x1021) & 0xFFFF; - } - } - } - return Common::swap16(static_cast<u16>(crc)); -} - -template <typename T> -T GetRandomValue(T min, T max) { - std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max)); - return static_cast<T>(distribution(gen)); -} - -template <typename T> -T GetRandomValue(T max) { - return GetRandomValue<T>({}, max); -} - -MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { - MiiStoreBitFields bf{}; - - if (gender == Gender::All) { - gender = GetRandomValue<Gender>(Gender::Maximum); - } - - bf.gender.Assign(gender); - bf.favorite_color.Assign(GetRandomValue<u8>(11)); - bf.region_move.Assign(0); - bf.font_region.Assign(FontRegion::Standard); - bf.type.Assign(0); - bf.height.Assign(64); - bf.build.Assign(64); - - if (age == Age::All) { - const auto temp{GetRandomValue<int>(10)}; - if (temp >= 8) { - age = Age::Old; - } else if (temp >= 4) { - age = Age::Normal; - } else { - age = Age::Young; - } - } - - if (race == Race::All) { - const auto temp{GetRandomValue<int>(10)}; - if (temp >= 8) { - race = Race::Black; - } else if (temp >= 4) { - race = Race::White; - } else { - race = Race::Asian; - } - } - - u32 axis_y{}; - if (gender == Gender::Female && age == Age::Young) { - axis_y = GetRandomValue<u32>(3); - } - - const std::size_t index{3 * static_cast<std::size_t>(age) + - 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)}; - - const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)}; - const auto faceline_color_info{RawData::RandomMiiFacelineColor.at( - 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))}; - const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)}; - const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)}; - const auto hair_type_info{RawData::RandomMiiHairType.at(index)}; - const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) + - static_cast<std::size_t>(age))}; - const auto eye_type_info{RawData::RandomMiiEyeType.at(index)}; - const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))}; - const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)}; - const auto nose_type_info{RawData::RandomMiiNoseType.at(index)}; - const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)}; - const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))}; - - bf.faceline_type.Assign( - faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]); - bf.faceline_color.Assign( - faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]); - bf.faceline_wrinkle.Assign( - faceline_wrinkle_info - .values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]); - bf.faceline_makeup.Assign( - faceline_makeup_info - .values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]); - - bf.hair_type.Assign( - hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]); - bf.hair_color.Assign( - HairColorLookup[hair_color_info - .values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]); - bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum)); - - bf.eye_type.Assign( - eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]); - - const auto eye_rotate_1{gender != Gender::Male ? 4 : 2}; - const auto eye_rotate_2{gender != Gender::Male ? 3 : 4}; - const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; - const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]}; - - bf.eye_color.Assign( - EyeColorLookup[eye_color_info - .values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]); - bf.eye_scale.Assign(4); - bf.eye_aspect.Assign(3); - bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate); - bf.eye_x.Assign(2); - bf.eye_y.Assign(axis_y + 12); - - bf.eyebrow_type.Assign( - eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]); - - const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0}; - const auto eyebrow_y{race == Race::Asian ? 9 : 10}; - const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6}; - const auto eyebrow_rotate{ - 32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]}; - - bf.eyebrow_color.Assign(bf.hair_color); - bf.eyebrow_scale.Assign(4); - bf.eyebrow_aspect.Assign(3); - bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate); - bf.eyebrow_x.Assign(2); - bf.eyebrow_y.Assign(axis_y + eyebrow_y); - - const auto nose_scale{gender == Gender::Female ? 3 : 4}; - - bf.nose_type.Assign( - nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]); - bf.nose_scale.Assign(nose_scale); - bf.nose_y.Assign(axis_y + 9); - - const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0}; - - bf.mouth_type.Assign( - mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]); - bf.mouth_color.Assign(MouthColorLookup[mouth_color]); - bf.mouth_scale.Assign(4); - bf.mouth_aspect.Assign(3); - bf.mouth_y.Assign(axis_y + 13); - - bf.beard_color.Assign(bf.hair_color); - bf.mustache_scale.Assign(4); - - if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) { - const auto mustache_and_beard_flag{ - GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)}; - - auto beard_type{BeardType::None}; - auto mustache_type{MustacheType::None}; - - if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) == - BeardAndMustacheFlag::Beard) { - beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5); - } - - if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == - BeardAndMustacheFlag::Mustache) { - mustache_type = - GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5); - } - - bf.mustache_type.Assign(mustache_type); - bf.beard_type.Assign(beard_type); - bf.mustache_y.Assign(10); - } else { - bf.mustache_type.Assign(MustacheType::None); - bf.beard_type.Assign(BeardType::None); - bf.mustache_y.Assign(axis_y + 10); - } - - const auto glasses_type_start{GetRandomValue<std::size_t>(100)}; - u8 glasses_type{}; - while (glasses_type_start < glasses_type_info.values[glasses_type]) { - if (++glasses_type >= glasses_type_info.values_count) { - ASSERT(false); - break; - } - } - - bf.glasses_type.Assign(glasses_type); - bf.glasses_color.Assign(GlassesColorLookup[0]); - bf.glasses_scale.Assign(4); - bf.glasses_y.Assign(axis_y + 10); - - bf.mole_type.Assign(0); - bf.mole_scale.Assign(4); - bf.mole_x.Assign(2); - bf.mole_y.Assign(20); - - return {DefaultMiiName, bf, user_id}; -} - -MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { - MiiStoreBitFields bf{}; - - bf.font_region.Assign(info.font_region); - bf.favorite_color.Assign(info.favorite_color); - bf.gender.Assign(info.gender); - bf.height.Assign(info.height); - bf.build.Assign(info.weight); - bf.type.Assign(info.type); - bf.region_move.Assign(info.region); - bf.faceline_type.Assign(info.face_type); - bf.faceline_color.Assign(info.face_color); - bf.faceline_wrinkle.Assign(info.face_wrinkle); - bf.faceline_makeup.Assign(info.face_makeup); - bf.hair_type.Assign(info.hair_type); - bf.hair_color.Assign(HairColorLookup[info.hair_color]); - bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip)); - bf.eye_type.Assign(info.eye_type); - bf.eye_color.Assign(EyeColorLookup[info.eye_color]); - bf.eye_scale.Assign(info.eye_scale); - bf.eye_aspect.Assign(info.eye_aspect); - bf.eye_rotate.Assign(info.eye_rotate); - bf.eye_x.Assign(info.eye_x); - bf.eye_y.Assign(info.eye_y); - bf.eyebrow_type.Assign(info.eyebrow_type); - bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]); - bf.eyebrow_scale.Assign(info.eyebrow_scale); - bf.eyebrow_aspect.Assign(info.eyebrow_aspect); - bf.eyebrow_rotate.Assign(info.eyebrow_rotate); - bf.eyebrow_x.Assign(info.eyebrow_x); - bf.eyebrow_y.Assign(info.eyebrow_y - 3); - bf.nose_type.Assign(info.nose_type); - bf.nose_scale.Assign(info.nose_scale); - bf.nose_y.Assign(info.nose_y); - bf.mouth_type.Assign(info.mouth_type); - bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]); - bf.mouth_scale.Assign(info.mouth_scale); - bf.mouth_aspect.Assign(info.mouth_aspect); - bf.mouth_y.Assign(info.mouth_y); - bf.beard_color.Assign(HairColorLookup[info.beard_color]); - bf.beard_type.Assign(static_cast<BeardType>(info.beard_type)); - bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type)); - bf.mustache_scale.Assign(info.mustache_scale); - bf.mustache_y.Assign(info.mustache_y); - bf.glasses_type.Assign(info.glasses_type); - bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]); - bf.glasses_scale.Assign(info.glasses_scale); - bf.glasses_y.Assign(info.glasses_y); - bf.mole_type.Assign(info.mole_type); - bf.mole_scale.Assign(info.mole_scale); - bf.mole_x.Assign(info.mole_x); - bf.mole_y.Assign(info.mole_y); - - return {DefaultMiiName, bf, user_id}; -} - -} // namespace - -MiiStoreData::MiiStoreData() = default; - -MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, - const Common::UUID& user_id) { - data.name = name; - data.uuid = Common::UUID::MakeRandomRFC4122V4(); - - std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); - data_crc = GenerateCrc16(data.data.data(), sizeof(data)); - device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID)); -} +MiiManager::MiiManager() {} -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 { @@ -397,301 +35,138 @@ bool MiiManager::IsFullDatabase() const { return false; } -u32 MiiManager::GetCount(SourceFlag source_flag) const { - std::size_t count{}; +u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const { + u32 mii_count{}; + if ((source_flag & SourceFlag::Default) != SourceFlag::None) { + mii_count += DefaultMiiCount; + } if ((source_flag & SourceFlag::Database) != SourceFlag::None) { // TODO(bunnei): We don't implement the Mii database, but when we do, update this - count += 0; } - if ((source_flag & SourceFlag::Default) != SourceFlag::None) { - count += (DefaultMiiCount - BaseMiiCount); - } - return static_cast<u32>(count); + return mii_count; } -Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) { +Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info, + const CharInfo& char_info, SourceFlag source_flag) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) { - return ERROR_CANNOT_FIND_ENTRY; + return ResultNotFound; } // TODO(bunnei): We don't implement the Mii database, so we can't have an entry - return ERROR_CANNOT_FIND_ENTRY; + return ResultNotFound; } -CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { - return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); +void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const { + StoreData store_data{}; + store_data.BuildDefault(index); + out_char_info.SetFromStoreData(store_data); } -CharInfo MiiManager::BuildDefault(std::size_t index) { - return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.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::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { - Service::Mii::MiiManager manager; - auto mii = manager.BuildDefault(0); - - if (!ValidateV3Info(mii_v3)) { - return mii; - } - - // TODO: We are ignoring a bunch of data from the mii_v3 - - mii.gender = static_cast<u8>(mii_v3.mii_information.gender); - mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color); - mii.height = mii_v3.height; - mii.build = mii_v3.build; - - // Copy name until string terminator - mii.name = {}; - for (std::size_t index = 0; index < mii.name.size() - 1; index++) { - mii.name[index] = mii_v3.mii_name[index]; - if (mii.name[index] == 0) { - break; - } - } - - mii.font_region = mii_v3.region_information.character_set; - - mii.faceline_type = mii_v3.appearance_bits1.face_shape; - mii.faceline_color = mii_v3.appearance_bits1.skin_color; - mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles; - mii.faceline_make = mii_v3.appearance_bits2.makeup; - - mii.hair_type = mii_v3.hair_style; - mii.hair_color = mii_v3.appearance_bits3.hair_color; - mii.hair_flip = mii_v3.appearance_bits3.flip_hair; - - mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type); - mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color); - mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale); - mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch); - mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation); - mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing); - mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position); - - mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style); - mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color); - mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale); - mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale); - mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation); - mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing); - mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position); - - mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type); - mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale); - mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position); - - mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type); - mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color); - mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale); - mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch); - mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position); - - mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type); - mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale); - mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position); - - mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type); - mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color); - - mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type); - mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color); - mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale); - mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position); - - mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled); - mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale); - mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position); - mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position); - - // TODO: Validate mii data - - return mii; +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); } -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; +void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const { + StoreData store_data{}; + mii_v3.BuildToStoreData(store_data); + out_char_info.SetFromStoreData(store_data); +} - // Copy name until string terminator - mii_v3.mii_name = {}; - for (std::size_t index = 0; index < mii.name.size() - 1; index++) { - mii_v3.mii_name[index] = mii.name[index]; - if (mii_v3.mii_name[index] == 0) { - break; - } +Result MiiManager::Get(const DatabaseSessionMetadata& metadata, + std::span<CharInfoElement> out_elements, u32& out_count, + SourceFlag source_flag) { + if ((source_flag & SourceFlag::Database) == SourceFlag::None) { + return BuildDefault(out_elements, out_count, source_flag); } - mii_v3.region_information.character_set.Assign(mii.font_region); + // TODO(bunnei): We don't implement the Mii database, so we can't have an entry - 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); + // Include default Mii at the end of the list + return BuildDefault(out_elements, out_count, source_flag); +} - mii_v3.hair_style = mii.hair_type; - mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); +Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info, + u32& out_count, SourceFlag source_flag) { + if ((source_flag & SourceFlag::Database) == SourceFlag::None) { + return BuildDefault(out_char_info, out_count, source_flag); + } - mii_v3.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); + // TODO(bunnei): We don't implement the Mii database, so we can't have an entry - 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); + // Include default Mii at the end of the list + return BuildDefault(out_char_info, out_count, source_flag); +} - 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); +Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, + SourceFlag source_flag) { + if ((source_flag & SourceFlag::Default) == SourceFlag::None) { + return ResultSuccess; + } - mii_v3.appearance_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); + StoreData store_data{}; - 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); + for (std::size_t index = 0; index < DefaultMiiCount; ++index) { + if (out_elements.size() <= static_cast<std::size_t>(out_count)) { + return ResultInvalidArgumentSize; + } - mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); + store_data.BuildDefault(static_cast<u32>(index)); - mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); - mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); + out_elements[out_count].source = Source::Default; + out_elements[out_count].char_info.SetFromStoreData(store_data); + out_count++; + } - 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); + return ResultSuccess; +} - // These types are converted to V3 from a table - mii_v3.appearance_bits1.skin_color.Assign(Ver3FacelineColorTable[mii.faceline_color]); - mii_v3.appearance_bits3.hair_color.Assign(Ver3HairColorTable[mii.hair_color]); - mii_v3.appearance_bits4.eye_color.Assign(Ver3EyeColorTable[mii.eye_color]); - mii_v3.appearance_bits5.eyebrow_color.Assign(Ver3HairColorTable[mii.eyebrow_color]); - mii_v3.appearance_bits7.mouth_color.Assign(Ver3MouthlineColorTable[mii.mouth_color]); - mii_v3.appearance_bits9.facial_hair_color.Assign(Ver3HairColorTable[mii.beard_color]); - mii_v3.appearance_bits10.glasses_color.Assign(Ver3GlassColorTable[mii.glasses_color]); - mii_v3.appearance_bits10.glasses_type.Assign(Ver3GlassTypeTable[mii.glasses_type]); +Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, + SourceFlag source_flag) { + if ((source_flag & SourceFlag::Default) == SourceFlag::None) { + return ResultSuccess; + } - mii_v3.crc = GenerateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16)); + StoreData store_data{}; - // TODO: Validate mii_v3 data + for (std::size_t index = 0; index < DefaultMiiCount; ++index) { + if (out_char_info.size() <= static_cast<std::size_t>(out_count)) { + return ResultInvalidArgumentSize; + } - return mii_v3; -} + store_data.BuildDefault(static_cast<u32>(index)); -NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const { - return { - .faceline_color = static_cast<u8>(mii.faceline_color & 0xf), - .hair_color = static_cast<u8>(mii.hair_color & 0x7f), - .eye_color = static_cast<u8>(mii.eyebrow_color & 0x7f), - .eyebrow_color = static_cast<u8>(mii.eyebrow_color & 0x7f), - .mouth_color = static_cast<u8>(mii.mouth_color & 0x7f), - .beard_color = static_cast<u8>(mii.beard_color & 0x7f), - .glass_color = static_cast<u8>(mii.glasses_color & 0x7f), - .glass_type = static_cast<u8>(mii.glasses_type & 0x1f), - }; -} + out_char_info[out_count].SetFromStoreData(store_data); + out_count++; + } -bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { - bool is_valid = mii_v3.version == 0 || mii_v3.version == 3; - - is_valid = is_valid && (mii_v3.mii_name[0] != 0); - - is_valid = is_valid && (mii_v3.mii_information.birth_month < 13); - is_valid = is_valid && (mii_v3.mii_information.birth_day < 32); - is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12); - is_valid = is_valid && (mii_v3.height < 128); - is_valid = is_valid && (mii_v3.build < 128); - - is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12); - is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7); - is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12); - is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12); - - is_valid = is_valid && (mii_v3.hair_style < 132); - is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8); - - is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60); - is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6); - is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8); - is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7); - is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8); - is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13); - is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19); - - is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25); - is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8); - is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9); - is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7); - is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12); - is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12); - is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19); - - is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18); - is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9); - is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19); - - is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36); - is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5); - is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9); - is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7); - is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19); - - is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6); - is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7); - is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17); - - is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6); - is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8); - - is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9); - is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6); - is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8); - is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21); - - is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2); - is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9); - is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17); - is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31); - - return is_valid; + return ResultSuccess; } -std::vector<MiiInfoElement> MiiManager::GetDefault(SourceFlag source_flag) { - std::vector<MiiInfoElement> result; +Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, + s32& out_index) { - if ((source_flag & SourceFlag::Default) == SourceFlag::None) { - return result; + if (char_info.Verify() != ValidationResult::NoErrors) { + return ResultInvalidCharInfo; } - for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) { - result.emplace_back(BuildDefault(index), Source::Default); - } - - return result; -} - -Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) { constexpr u32 INVALID_INDEX{0xFFFFFFFF}; - index = INVALID_INDEX; + out_index = INVALID_INDEX; // TODO(bunnei): We don't implement the Mii database, so we can't have an index - return ERROR_CANNOT_FIND_ENTRY; + return ResultNotFound; +} + +void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) { + metadata.interface_version = version; } } // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 45c2be3c8..a2e7a6d73 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -6,7 +6,10 @@ #include <vector> #include "core/hle/result.h" -#include "core/hle/service/mii/types.h" +#include "core/hle/service/mii/mii_types.h" +#include "core/hle/service/mii/types/char_info.h" +#include "core/hle/service/mii/types/store_data.h" +#include "core/hle/service/mii/types/ver3_store_data.h" namespace Service::Mii { @@ -16,25 +19,30 @@ class MiiManager { public: MiiManager(); - bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); - 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 BuildDefault(std::size_t index); - CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; - bool ValidateV3Info(const Ver3StoreData& mii_v3) const; - std::vector<MiiInfoElement> GetDefault(SourceFlag source_flag); - Result GetIndex(const CharInfo& info, u32& index); - - // This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData - Ver3StoreData BuildFromStoreData(const CharInfo& mii) const; + bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const; - // This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData - NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const; + bool IsFullDatabase() const; + u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const; + Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info, + const CharInfo& char_info, SourceFlag source_flag); + Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements, + u32& out_count, SourceFlag source_flag); + Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info, + u32& out_count, SourceFlag source_flag); + void BuildDefault(CharInfo& out_char_info, u32 index) const; + void BuildBase(CharInfo& out_char_info, Gender gender) const; + void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const; + void ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const; + std::vector<CharInfoElement> GetDefault(SourceFlag source_flag); + Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, + s32& out_index); + void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version); private: - const Common::UUID user_id{}; + Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, + SourceFlag source_flag); + Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag); + u64 update_counter{}; }; diff --git a/src/core/hle/service/mii/mii_result.h b/src/core/hle/service/mii/mii_result.h new file mode 100644 index 000000000..021cb76da --- /dev/null +++ b/src/core/hle/service/mii/mii_result.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Mii { + +constexpr Result ResultInvalidArgument{ErrorModule::Mii, 1}; +constexpr Result ResultInvalidArgumentSize{ErrorModule::Mii, 2}; +constexpr Result ResultNotUpdated{ErrorModule::Mii, 3}; +constexpr Result ResultNotFound{ErrorModule::Mii, 4}; +constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5}; +constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100}; +constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109}; +constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202}; +constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203}; + +}; // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h new file mode 100644 index 000000000..95476f745 --- /dev/null +++ b/src/core/hle/service/mii/mii_types.h @@ -0,0 +1,694 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <type_traits> + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/uuid.h" + +namespace Service::Mii { + +constexpr u8 MaxHeight = 127; +constexpr u8 MaxBuild = 127; +constexpr u8 MaxType = 1; +constexpr u8 MaxRegionMove = 3; +constexpr u8 MaxEyeScale = 7; +constexpr u8 MaxEyeAspect = 6; +constexpr u8 MaxEyeRotate = 7; +constexpr u8 MaxEyeX = 12; +constexpr u8 MaxEyeY = 18; +constexpr u8 MaxEyebrowScale = 8; +constexpr u8 MaxEyebrowAspect = 6; +constexpr u8 MaxEyebrowRotate = 11; +constexpr u8 MaxEyebrowX = 12; +constexpr u8 MaxEyebrowY = 18; +constexpr u8 MaxNoseScale = 8; +constexpr u8 MaxNoseY = 18; +constexpr u8 MaxMouthScale = 8; +constexpr u8 MaxMoutAspect = 6; +constexpr u8 MaxMouthY = 18; +constexpr u8 MaxMustacheScale = 8; +constexpr u8 MasMustacheY = 16; +constexpr u8 MaxGlassScale = 7; +constexpr u8 MaxGlassY = 20; +constexpr u8 MaxMoleScale = 8; +constexpr u8 MaxMoleX = 16; +constexpr u8 MaxMoleY = 30; +constexpr u8 MaxVer3CommonColor = 7; +constexpr u8 MaxVer3GlassType = 8; + +enum class Age : u8 { + Young, + Normal, + Old, + All, // Default + + Max = All, +}; + +enum class Gender : u8 { + Male, + Female, + All, // Default + + Max = Female, +}; + +enum class Race : u8 { + Black, + White, + Asian, + All, // Default + + Max = All, +}; + +enum class HairType : u8 { + NormalLong, // Default + NormalShort, + NormalMedium, + NormalExtraLong, + NormalLongBottom, + NormalTwoPeaks, + PartingLong, + FrontLock, + PartingShort, + PartingExtraLongCurved, + PartingExtraLong, + PartingMiddleLong, + PartingSquared, + PartingLongBottom, + PeaksTop, + PeaksSquared, + PartingPeaks, + PeaksLongBottom, + Peaks, + PeaksRounded, + PeaksSide, + PeaksMedium, + PeaksLong, + PeaksRoundedLong, + PartingFrontPeaks, + PartingLongFront, + PartingLongRounded, + PartingFrontPeaksLong, + PartingExtraLongRounded, + LongRounded, + NormalUnknown1, + NormalUnknown2, + NormalUnknown3, + NormalUnknown4, + NormalUnknown5, + NormalUnknown6, + DreadLocks, + PlatedMats, + Caps, + Afro, + PlatedMatsLong, + Beanie, + Short, + ShortTopLongSide, + ShortUnknown1, + ShortUnknown2, + MilitaryParting, + Military, + ShortUnknown3, + ShortUnknown4, + ShortUnknown5, + ShortUnknown6, + NoneTop, + None, + LongUnknown1, + LongUnknown2, + LongUnknown3, + LongUnknown4, + LongUnknown5, + LongUnknown6, + LongUnknown7, + LongUnknown8, + LongUnknown9, + LongUnknown10, + LongUnknown11, + LongUnknown12, + LongUnknown13, + LongUnknown14, + LongUnknown15, + LongUnknown16, + LongUnknown17, + LongUnknown18, + LongUnknown19, + LongUnknown20, + LongUnknown21, + LongUnknown22, + LongUnknown23, + LongUnknown24, + LongUnknown25, + LongUnknown26, + LongUnknown27, + LongUnknown28, + LongUnknown29, + LongUnknown30, + LongUnknown31, + LongUnknown32, + LongUnknown33, + LongUnknown34, + LongUnknown35, + LongUnknown36, + LongUnknown37, + LongUnknown38, + LongUnknown39, + LongUnknown40, + LongUnknown41, + LongUnknown42, + LongUnknown43, + LongUnknown44, + LongUnknown45, + LongUnknown46, + LongUnknown47, + LongUnknown48, + LongUnknown49, + LongUnknown50, + LongUnknown51, + LongUnknown52, + LongUnknown53, + LongUnknown54, + LongUnknown55, + LongUnknown56, + LongUnknown57, + LongUnknown58, + LongUnknown59, + LongUnknown60, + LongUnknown61, + LongUnknown62, + LongUnknown63, + LongUnknown64, + LongUnknown65, + LongUnknown66, + TwoMediumFrontStrandsOneLongBackPonyTail, + TwoFrontStrandsLongBackPonyTail, + PartingFrontTwoLongBackPonyTails, + TwoFrontStrandsOneLongBackPonyTail, + LongBackPonyTail, + LongFrontTwoLongBackPonyTails, + StrandsTwoShortSidedPonyTails, + TwoMediumSidedPonyTails, + ShortFrontTwoBackPonyTails, + TwoShortSidedPonyTails, + TwoLongSidedPonyTails, + LongFrontTwoBackPonyTails, + + Max = LongFrontTwoBackPonyTails, +}; + +enum class MoleType : u8 { + None, // Default + OneDot, + + Max = OneDot, +}; + +enum class HairFlip : u8 { + Left, // Default + Right, + + Max = Right, +}; + +enum class CommonColor : u8 { + // For simplicity common colors aren't listed + Max = 99, + Count = 100, +}; + +enum class FavoriteColor : u8 { + Red, // Default + Orange, + Yellow, + LimeGreen, + Green, + Blue, + LightBlue, + Pink, + Purple, + Brown, + White, + Black, + + Max = Black, +}; + +enum class EyeType : u8 { + Normal, // Default + NormalLash, + WhiteLash, + WhiteNoBottom, + OvalAngledWhite, + AngryWhite, + DotLashType1, + Line, + DotLine, + OvalWhite, + RoundedWhite, + NormalShadow, + CircleWhite, + Circle, + CircleWhiteStroke, + NormalOvalNoBottom, + NormalOvalLarge, + NormalRoundedNoBottom, + SmallLash, + Small, + TwoSmall, + NormalLongLash, + WhiteTwoLashes, + WhiteThreeLashes, + DotAngry, + DotAngled, + Oval, + SmallWhite, + WhiteAngledNoBottom, + WhiteAngledNoLeft, + SmallWhiteTwoLashes, + LeafWhiteLash, + WhiteLargeNoBottom, + Dot, + DotLashType2, + DotThreeLashes, + WhiteOvalTop, + WhiteOvalBottom, + WhiteOvalBottomFlat, + WhiteOvalTwoLashes, + WhiteOvalThreeLashes, + WhiteOvalNoBottomTwoLashes, + DotWhite, + WhiteOvalTopFlat, + WhiteThinLeaf, + StarThreeLashes, + LineTwoLashes, + CrowsFeet, + WhiteNoBottomFlat, + WhiteNoBottomRounded, + WhiteSmallBottomLine, + WhiteNoBottomLash, + WhiteNoPartialBottomLash, + WhiteOvalBottomLine, + WhiteNoBottomLashTopLine, + WhiteNoPartialBottomTwoLashes, + NormalTopLine, + WhiteOvalLash, + RoundTired, + WhiteLarge, + + Max = WhiteLarge, +}; + +enum class MouthType : u8 { + Neutral, // Default + NeutralLips, + Smile, + SmileStroke, + SmileTeeth, + LipsSmall, + LipsLarge, + Wave, + WaveAngrySmall, + NeutralStrokeLarge, + TeethSurprised, + LipsExtraLarge, + LipsUp, + NeutralDown, + Surprised, + TeethMiddle, + NeutralStroke, + LipsExtraSmall, + Malicious, + LipsDual, + NeutralComma, + NeutralUp, + TeethLarge, + WaveAngry, + LipsSexy, + SmileInverted, + LipsSexyOutline, + SmileRounded, + LipsTeeth, + NeutralOpen, + TeethRounded, + WaveAngrySmallInverted, + NeutralCommaInverted, + TeethFull, + SmileDownLine, + Kiss, + + Max = Kiss, +}; + +enum class FontRegion : u8 { + Standard, // Default + China, + Korea, + Taiwan, + + Max = Taiwan, +}; + +enum class FacelineType : u8 { + Sharp, // Default + Rounded, + SharpRounded, + SharpRoundedSmall, + Large, + LargeRounded, + SharpSmall, + Flat, + Bump, + Angular, + FlatRounded, + AngularSmall, + + Max = AngularSmall, +}; + +enum class FacelineColor : u8 { + Beige, // Default + WarmBeige, + Natural, + Honey, + Chestnut, + Porcelain, + Ivory, + WarmIvory, + Almond, + Espresso, + + Max = Espresso, + Count = Max + 1, +}; + +enum class FacelineWrinkle : u8 { + None, // Default + TearTroughs, + FacialPain, + Cheeks, + Folds, + UnderTheEyes, + SplitChin, + Chin, + BrowDroop, + MouthFrown, + CrowsFeet, + FoldsCrowsFrown, + + Max = FoldsCrowsFrown, +}; + +enum class FacelineMake : u8 { + None, // Default + CheekPorcelain, + CheekNatural, + EyeShadowBlue, + CheekBlushPorcelain, + CheekBlushNatural, + CheekPorcelainEyeShadowBlue, + CheekPorcelainEyeShadowNatural, + CheekBlushPorcelainEyeShadowEspresso, + Freckles, + LionsManeBeard, + StubbleBeard, + + Max = StubbleBeard, +}; + +enum class EyebrowType : u8 { + FlatAngledLarge, // Default + LowArchRoundedThin, + SoftAngledLarge, + MediumArchRoundedThin, + RoundedMedium, + LowArchMedium, + RoundedThin, + UpThin, + MediumArchRoundedMedium, + RoundedLarge, + UpLarge, + FlatAngledLargeInverted, + MediumArchFlat, + AngledThin, + HorizontalLarge, + HighArchFlat, + Flat, + MediumArchLarge, + LowArchThin, + RoundedThinInverted, + HighArchLarge, + Hairy, + Dotted, + None, + + Max = None, +}; + +enum class NoseType : u8 { + Normal, // Default + Rounded, + Dot, + Arrow, + Roman, + Triangle, + Button, + RoundedInverted, + Potato, + Grecian, + Snub, + Aquiline, + ArrowLeft, + RoundedLarge, + Hooked, + Fat, + Droopy, + ArrowLarge, + + Max = ArrowLarge, +}; + +enum class BeardType : u8 { + None, + Goatee, + GoateeLong, + LionsManeLong, + LionsMane, + Full, + + Min = Goatee, + Max = Full, +}; + +enum class MustacheType : u8 { + None, + Walrus, + Pencil, + Horseshoe, + Normal, + Toothbrush, + + Min = Walrus, + Max = Toothbrush, +}; + +enum class GlassType : u8 { + None, + Oval, + Wayfarer, + Rectangle, + TopRimless, + Rounded, + Oversized, + CatEye, + Square, + BottomRimless, + SemiOpaqueRounded, + SemiOpaqueCatEye, + SemiOpaqueOval, + SemiOpaqueRectangle, + SemiOpaqueAviator, + OpaqueRounded, + OpaqueCatEye, + OpaqueOval, + OpaqueRectangle, + OpaqueAviator, + + Max = OpaqueAviator, + Count = Max + 1, +}; + +enum class BeardAndMustacheFlag : u32 { + Beard = 1, + Mustache, + All = Beard | Mustache, +}; +DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); + +enum class Source : u32 { + Database = 0, + Default = 1, + Account = 2, + Friend = 3, +}; + +enum class SourceFlag : u32 { + None = 0, + Database = 1 << 0, + Default = 1 << 1, +}; +DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); + +enum class ValidationResult : u32 { + NoErrors = 0x0, + InvalidBeardColor = 0x1, + InvalidBeardType = 0x2, + InvalidBuild = 0x3, + InvalidEyeAspect = 0x4, + InvalidEyeColor = 0x5, + InvalidEyeRotate = 0x6, + InvalidEyeScale = 0x7, + InvalidEyeType = 0x8, + InvalidEyeX = 0x9, + InvalidEyeY = 0xa, + InvalidEyebrowAspect = 0xb, + InvalidEyebrowColor = 0xc, + InvalidEyebrowRotate = 0xd, + InvalidEyebrowScale = 0xe, + InvalidEyebrowType = 0xf, + InvalidEyebrowX = 0x10, + InvalidEyebrowY = 0x11, + InvalidFacelineColor = 0x12, + InvalidFacelineMake = 0x13, + InvalidFacelineWrinkle = 0x14, + InvalidFacelineType = 0x15, + InvalidColor = 0x16, + InvalidFont = 0x17, + InvalidGender = 0x18, + InvalidGlassColor = 0x19, + InvalidGlassScale = 0x1a, + InvalidGlassType = 0x1b, + InvalidGlassY = 0x1c, + InvalidHairColor = 0x1d, + InvalidHairFlip = 0x1e, + InvalidHairType = 0x1f, + InvalidHeight = 0x20, + InvalidMoleScale = 0x21, + InvalidMoleType = 0x22, + InvalidMoleX = 0x23, + InvalidMoleY = 0x24, + InvalidMouthAspect = 0x25, + InvalidMouthColor = 0x26, + InvalidMouthScale = 0x27, + InvalidMouthType = 0x28, + InvalidMouthY = 0x29, + InvalidMustacheScale = 0x2a, + InvalidMustacheType = 0x2b, + InvalidMustacheY = 0x2c, + InvalidNoseScale = 0x2e, + InvalidNoseType = 0x2f, + InvalidNoseY = 0x30, + InvalidRegionMove = 0x31, + InvalidCreateId = 0x32, + InvalidName = 0x33, + InvalidType = 0x35, +}; + +struct Nickname { + static constexpr std::size_t MaxNameSize = 10; + std::array<char16_t, MaxNameSize> data; + + // Checks for null, non-zero terminated or dirty strings + bool IsValid() const { + if (data[0] == 0) { + return false; + } + + if (data[MaxNameSize] != 0) { + return false; + } + std::size_t index = 1; + while (data[index] != 0) { + index++; + } + while (index < MaxNameSize && data[index] == 0) { + index++; + } + return index == MaxNameSize; + } +}; +static_assert(sizeof(Nickname) == 0x14, "Nickname is an invalid size"); + +struct DefaultMii { + u32 face_type{}; + u32 face_color{}; + u32 face_wrinkle{}; + u32 face_makeup{}; + u32 hair_type{}; + u32 hair_color{}; + u32 hair_flip{}; + u32 eye_type{}; + u32 eye_color{}; + u32 eye_scale{}; + u32 eye_aspect{}; + u32 eye_rotate{}; + u32 eye_x{}; + u32 eye_y{}; + u32 eyebrow_type{}; + u32 eyebrow_color{}; + u32 eyebrow_scale{}; + u32 eyebrow_aspect{}; + u32 eyebrow_rotate{}; + u32 eyebrow_x{}; + u32 eyebrow_y{}; + u32 nose_type{}; + u32 nose_scale{}; + u32 nose_y{}; + u32 mouth_type{}; + u32 mouth_color{}; + u32 mouth_scale{}; + u32 mouth_aspect{}; + u32 mouth_y{}; + u32 mustache_type{}; + u32 beard_type{}; + u32 beard_color{}; + u32 mustache_scale{}; + u32 mustache_y{}; + u32 glasses_type{}; + u32 glasses_color{}; + u32 glasses_scale{}; + u32 glasses_y{}; + u32 mole_type{}; + u32 mole_scale{}; + u32 mole_x{}; + u32 mole_y{}; + u32 height{}; + u32 weight{}; + u32 gender{}; + u32 favorite_color{}; + u32 region_move{}; + u32 font_region{}; + u32 type{}; + Nickname nickname; +}; +static_assert(sizeof(DefaultMii) == 0xd8, "DefaultMii has incorrect size."); + +struct DatabaseSessionMetadata { + u32 interface_version; + u32 magic; + u64 update_counter; + + bool IsInterfaceVersionSupported(u32 version) const { + return version <= interface_version; + } +}; + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h new file mode 100644 index 000000000..ddb544c23 --- /dev/null +++ b/src/core/hle/service/mii/mii_util.h @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <random> +#include <span> + +#include "common/common_types.h" +#include "common/swap.h" +#include "common/uuid.h" +#include "core/hle/service/mii/mii_types.h" + +namespace Service::Mii { +class MiiUtil { +public: + static u16 CalculateCrc16(const void* data, std::size_t size) { + s32 crc{}; + for (std::size_t i = 0; i < size; i++) { + crc ^= static_cast<const u8*>(data)[i] << 8; + for (std::size_t j = 0; j < 8; j++) { + crc <<= 1; + if ((crc & 0x10000) != 0) { + crc = (crc ^ 0x1021) & 0xFFFF; + } + } + } + return Common::swap16(static_cast<u16>(crc)); + } + + static Common::UUID MakeCreateId() { + return Common::UUID::MakeRandomRFC4122V4(); + } + + static Common::UUID GetDeviceId() { + // This should be nn::settings::detail::GetMiiAuthorId() + return Common::UUID::MakeDefault(); + } + + template <typename T> + static T GetRandomValue(T min, T max) { + std::random_device device; + std::mt19937 gen(device()); + std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), + static_cast<u64>(max)); + return static_cast<T>(distribution(gen)); + } + + template <typename T> + static T GetRandomValue(T max) { + return GetRandomValue<T>({}, max); + } + + static bool IsFontRegionValid(FontRegion font, std::span<const char16_t> text) { + // TODO: This function needs to check against the font tables + return true; + } +}; +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h deleted file mode 100644 index c2bec68d4..000000000 --- a/src/core/hle/service/mii/raw_data.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "core/hle/service/mii/types.h" - -namespace Service::Mii::RawData { - -extern const std::array<Service::Mii::DefaultMii, 8> DefaultMii; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline; -extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType; -extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType; -extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType; -extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType; -extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType; - -} // namespace Service::Mii::RawData diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h deleted file mode 100644 index c48d08d79..000000000 --- a/src/core/hle/service/mii/types.h +++ /dev/null @@ -1,553 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <type_traits> - -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/uuid.h" - -namespace Service::Mii { - -enum class Age : u32 { - Young, - Normal, - Old, - All, -}; - -enum class BeardType : u32 { - None, - Beard1, - Beard2, - Beard3, - Beard4, - Beard5, -}; - -enum class BeardAndMustacheFlag : u32 { - Beard = 1, - Mustache, - All = Beard | Mustache, -}; -DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); - -enum class FontRegion : u32 { - Standard, - China, - Korea, - Taiwan, -}; - -enum class Gender : u32 { - Male, - Female, - All, - Maximum = Female, -}; - -enum class HairFlip : u32 { - Left, - Right, - Maximum = Right, -}; - -enum class MustacheType : u32 { - None, - Mustache1, - Mustache2, - Mustache3, - Mustache4, - Mustache5, -}; - -enum class Race : u32 { - Black, - White, - Asian, - All, -}; - -enum class Source : u32 { - Database = 0, - Default = 1, - Account = 2, - Friend = 3, -}; - -enum class SourceFlag : u32 { - None = 0, - Database = 1 << 0, - Default = 1 << 1, -}; -DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); - -// nn::mii::CharInfo -struct CharInfo { - Common::UUID uuid; - std::array<char16_t, 11> name; - u8 font_region; - u8 favorite_color; - u8 gender; - u8 height; - u8 build; - u8 type; - u8 region_move; - u8 faceline_type; - u8 faceline_color; - u8 faceline_wrinkle; - u8 faceline_make; - u8 hair_type; - u8 hair_color; - u8 hair_flip; - u8 eye_type; - u8 eye_color; - u8 eye_scale; - u8 eye_aspect; - u8 eye_rotate; - u8 eye_x; - u8 eye_y; - u8 eyebrow_type; - u8 eyebrow_color; - u8 eyebrow_scale; - u8 eyebrow_aspect; - u8 eyebrow_rotate; - u8 eyebrow_x; - u8 eyebrow_y; - u8 nose_type; - u8 nose_scale; - u8 nose_y; - u8 mouth_type; - u8 mouth_color; - u8 mouth_scale; - u8 mouth_aspect; - u8 mouth_y; - u8 beard_color; - u8 beard_type; - u8 mustache_type; - u8 mustache_scale; - u8 mustache_y; - u8 glasses_type; - u8 glasses_color; - u8 glasses_scale; - u8 glasses_y; - u8 mole_type; - u8 mole_scale; - u8 mole_x; - u8 mole_y; - u8 padding; -}; -static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); -static_assert(std::has_unique_object_representations_v<CharInfo>, - "All bits of CharInfo must contribute to its value."); - -#pragma pack(push, 4) - -struct MiiInfoElement { - MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {} - - CharInfo info{}; - Source source{}; -}; -static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); - -struct MiiStoreBitFields { - union { - u32 word_0{}; - - BitField<0, 8, u32> hair_type; - BitField<8, 7, u32> height; - BitField<15, 1, u32> mole_type; - BitField<16, 7, u32> build; - BitField<23, 1, HairFlip> hair_flip; - BitField<24, 7, u32> hair_color; - BitField<31, 1, u32> type; - }; - - union { - u32 word_1{}; - - BitField<0, 7, u32> eye_color; - BitField<7, 1, Gender> gender; - BitField<8, 7, u32> eyebrow_color; - BitField<16, 7, u32> mouth_color; - BitField<24, 7, u32> beard_color; - }; - - union { - u32 word_2{}; - - BitField<0, 7, u32> glasses_color; - BitField<8, 6, u32> eye_type; - BitField<14, 2, u32> region_move; - BitField<16, 6, u32> mouth_type; - BitField<22, 2, FontRegion> font_region; - BitField<24, 5, u32> eye_y; - BitField<29, 3, u32> glasses_scale; - }; - - union { - u32 word_3{}; - - BitField<0, 5, u32> eyebrow_type; - BitField<5, 3, MustacheType> mustache_type; - BitField<8, 5, u32> nose_type; - BitField<13, 3, BeardType> beard_type; - BitField<16, 5, u32> nose_y; - BitField<21, 3, u32> mouth_aspect; - BitField<24, 5, u32> mouth_y; - BitField<29, 3, u32> eyebrow_aspect; - }; - - union { - u32 word_4{}; - - BitField<0, 5, u32> mustache_y; - BitField<5, 3, u32> eye_rotate; - BitField<8, 5, u32> glasses_y; - BitField<13, 3, u32> eye_aspect; - BitField<16, 5, u32> mole_x; - BitField<21, 3, u32> eye_scale; - BitField<24, 5, u32> mole_y; - }; - - union { - u32 word_5{}; - - BitField<0, 5, u32> glasses_type; - BitField<8, 4, u32> favorite_color; - BitField<12, 4, u32> faceline_type; - BitField<16, 4, u32> faceline_color; - BitField<20, 4, u32> faceline_wrinkle; - BitField<24, 4, u32> faceline_makeup; - BitField<28, 4, u32> eye_x; - }; - - union { - u32 word_6{}; - - BitField<0, 4, u32> eyebrow_scale; - BitField<4, 4, u32> eyebrow_rotate; - BitField<8, 4, u32> eyebrow_x; - BitField<12, 4, u32> eyebrow_y; - BitField<16, 4, u32> nose_scale; - BitField<20, 4, u32> mouth_scale; - BitField<24, 4, u32> mustache_scale; - BitField<28, 4, u32> mole_scale; - }; -}; -static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size."); -static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, - "MiiStoreBitFields is not trivially copyable."); - -// This is nn::mii::Ver3StoreData -// Based on citra HLE::Applets::MiiData and PretendoNetwork. -// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48 -// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299 -struct Ver3StoreData { - u8 version; - union { - u8 raw; - - BitField<0, 1, u8> allow_copying; - BitField<1, 1, u8> profanity_flag; - BitField<2, 2, u8> region_lock; - BitField<4, 2, u8> character_set; - } region_information; - u16_be mii_id; - u64_be system_id; - u32_be specialness_and_creation_date; - std::array<u8, 0x6> creator_mac; - u16_be padding; - union { - u16 raw; - - BitField<0, 1, u16> gender; - BitField<1, 4, u16> birth_month; - BitField<5, 5, u16> birth_day; - BitField<10, 4, u16> favorite_color; - BitField<14, 1, u16> favorite; - } mii_information; - std::array<char16_t, 0xA> mii_name; - u8 height; - u8 build; - union { - u8 raw; - - BitField<0, 1, u8> disable_sharing; - BitField<1, 4, u8> face_shape; - BitField<5, 3, u8> skin_color; - } appearance_bits1; - union { - u8 raw; - - BitField<0, 4, u8> wrinkles; - BitField<4, 4, u8> makeup; - } appearance_bits2; - u8 hair_style; - union { - u8 raw; - - BitField<0, 3, u8> hair_color; - BitField<3, 1, u8> flip_hair; - } appearance_bits3; - union { - u32 raw; - - BitField<0, 6, u32> eye_type; - BitField<6, 3, u32> eye_color; - BitField<9, 4, u32> eye_scale; - BitField<13, 3, u32> eye_vertical_stretch; - BitField<16, 5, u32> eye_rotation; - BitField<21, 4, u32> eye_spacing; - BitField<25, 5, u32> eye_y_position; - } appearance_bits4; - union { - u32 raw; - - BitField<0, 5, u32> eyebrow_style; - BitField<5, 3, u32> eyebrow_color; - BitField<8, 4, u32> eyebrow_scale; - BitField<12, 3, u32> eyebrow_yscale; - BitField<16, 4, u32> eyebrow_rotation; - BitField<21, 4, u32> eyebrow_spacing; - BitField<25, 5, u32> eyebrow_y_position; - } appearance_bits5; - union { - u16 raw; - - BitField<0, 5, u16> nose_type; - BitField<5, 4, u16> nose_scale; - BitField<9, 5, u16> nose_y_position; - } appearance_bits6; - union { - u16 raw; - - BitField<0, 6, u16> mouth_type; - BitField<6, 3, u16> mouth_color; - BitField<9, 4, u16> mouth_scale; - BitField<13, 3, u16> mouth_horizontal_stretch; - } appearance_bits7; - union { - u8 raw; - - BitField<0, 5, u8> mouth_y_position; - BitField<5, 3, u8> mustache_type; - } appearance_bits8; - u8 allow_copying; - union { - u16 raw; - - BitField<0, 3, u16> bear_type; - BitField<3, 3, u16> facial_hair_color; - BitField<6, 4, u16> mustache_scale; - BitField<10, 5, u16> mustache_y_position; - } appearance_bits9; - union { - u16 raw; - - BitField<0, 4, u16> glasses_type; - BitField<4, 3, u16> glasses_color; - BitField<7, 4, u16> glasses_scale; - BitField<11, 5, u16> glasses_y_position; - } appearance_bits10; - union { - u16 raw; - - BitField<0, 1, u16> mole_enabled; - BitField<1, 4, u16> mole_scale; - BitField<5, 5, u16> mole_x_position; - BitField<10, 5, u16> mole_y_position; - } appearance_bits11; - - std::array<u16_le, 0xA> author_name; - INSERT_PADDING_BYTES(0x2); - u16_be crc; -}; -static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size"); - -struct NfpStoreDataExtension { - u8 faceline_color; - u8 hair_color; - u8 eye_color; - u8 eyebrow_color; - u8 mouth_color; - u8 beard_color; - u8 glass_color; - u8 glass_type; -}; -static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size"); - -constexpr std::array<u8, 0x10> Ver3FacelineColorTable{ - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, -}; - -constexpr std::array<u8, 100> Ver3HairColorTable{ - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0, - 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4, - 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, - 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7, - 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4, -}; - -constexpr std::array<u8, 100> Ver3EyeColorTable{ - 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4, - 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4, - 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, - 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, - 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, -}; - -constexpr std::array<u8, 100> Ver3MouthlineColorTable{ - 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, - 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, - 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, - 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, - 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, - 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3, -}; - -constexpr std::array<u8, 100> Ver3GlassColorTable{ - 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3, - 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, - 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5, - 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4, - 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, -}; - -constexpr std::array<u8, 20> Ver3GlassTypeTable{ - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, - 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7, -}; - -struct MiiStoreData { - using Name = std::array<char16_t, 10>; - - MiiStoreData(); - MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields, - const Common::UUID& user_id); - - // This corresponds to the above structure MiiStoreBitFields. I did it like this because the - // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is - // not suitable for our uses. - struct { - std::array<u8, 0x1C> data{}; - static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); - - Name name{}; - Common::UUID uuid{}; - } data; - - u16 data_crc{}; - u16 device_crc{}; -}; -static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); - -struct MiiStoreDataElement { - MiiStoreData data{}; - Source source{}; -}; -static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); - -struct MiiDatabase { - u32 magic{}; // 'NFDB' - std::array<MiiStoreData, 0x64> miis{}; - INSERT_PADDING_BYTES(1); - u8 count{}; - u16 crc{}; -}; -static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); - -struct RandomMiiValues { - std::array<u8, 0xbc> values{}; -}; -static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); - -struct RandomMiiData4 { - Gender gender{}; - Age age{}; - Race race{}; - u32 values_count{}; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); - -struct RandomMiiData3 { - u32 arg_1; - u32 arg_2; - u32 values_count; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); - -struct RandomMiiData2 { - u32 arg_1; - u32 values_count; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); - -struct DefaultMii { - u32 face_type{}; - u32 face_color{}; - u32 face_wrinkle{}; - u32 face_makeup{}; - u32 hair_type{}; - u32 hair_color{}; - u32 hair_flip{}; - u32 eye_type{}; - u32 eye_color{}; - u32 eye_scale{}; - u32 eye_aspect{}; - u32 eye_rotate{}; - u32 eye_x{}; - u32 eye_y{}; - u32 eyebrow_type{}; - u32 eyebrow_color{}; - u32 eyebrow_scale{}; - u32 eyebrow_aspect{}; - u32 eyebrow_rotate{}; - u32 eyebrow_x{}; - u32 eyebrow_y{}; - u32 nose_type{}; - u32 nose_scale{}; - u32 nose_y{}; - u32 mouth_type{}; - u32 mouth_color{}; - u32 mouth_scale{}; - u32 mouth_aspect{}; - u32 mouth_y{}; - u32 mustache_type{}; - u32 beard_type{}; - u32 beard_color{}; - u32 mustache_scale{}; - u32 mustache_y{}; - u32 glasses_type{}; - u32 glasses_color{}; - u32 glasses_scale{}; - u32 glasses_y{}; - u32 mole_type{}; - u32 mole_scale{}; - u32 mole_x{}; - u32 mole_y{}; - u32 height{}; - u32 weight{}; - Gender gender{}; - u32 favorite_color{}; - u32 region{}; - FontRegion font_region{}; - u32 type{}; - INSERT_PADDING_WORDS(5); -}; -static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); - -#pragma pack(pop) - -} // namespace Service::Mii diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp new file mode 100644 index 000000000..bb948c628 --- /dev/null +++ b/src/core/hle/service/mii/types/char_info.cpp @@ -0,0 +1,482 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/mii/types/char_info.h" +#include "core/hle/service/mii/types/store_data.h" + +namespace Service::Mii { + +void CharInfo::SetFromStoreData(const StoreData& store_data) { + name = store_data.GetNickname(); + null_terminator = '\0'; + create_id = store_data.GetCreateId(); + font_region = store_data.GetFontRegion(); + favorite_color = store_data.GetFavoriteColor(); + gender = store_data.GetGender(); + height = store_data.GetHeight(); + build = store_data.GetBuild(); + type = store_data.GetType(); + region_move = store_data.GetRegionMove(); + faceline_type = store_data.GetFacelineType(); + faceline_color = store_data.GetFacelineColor(); + faceline_wrinkle = store_data.GetFacelineWrinkle(); + faceline_make = store_data.GetFacelineMake(); + hair_type = store_data.GetHairType(); + hair_color = store_data.GetHairColor(); + hair_flip = store_data.GetHairFlip(); + eye_type = store_data.GetEyeType(); + eye_color = store_data.GetEyeColor(); + eye_scale = store_data.GetEyeScale(); + eye_aspect = store_data.GetEyeAspect(); + eye_rotate = store_data.GetEyeRotate(); + eye_x = store_data.GetEyeX(); + eye_y = store_data.GetEyeY(); + eyebrow_type = store_data.GetEyebrowType(); + eyebrow_color = store_data.GetEyebrowColor(); + eyebrow_scale = store_data.GetEyebrowScale(); + eyebrow_aspect = store_data.GetEyebrowAspect(); + eyebrow_rotate = store_data.GetEyebrowRotate(); + eyebrow_x = store_data.GetEyebrowX(); + eyebrow_y = store_data.GetEyebrowY(); + nose_type = store_data.GetNoseType(); + nose_scale = store_data.GetNoseScale(); + nose_y = store_data.GetNoseY(); + mouth_type = store_data.GetMouthType(); + mouth_color = store_data.GetMouthColor(); + mouth_scale = store_data.GetMouthScale(); + mouth_aspect = store_data.GetMouthAspect(); + mouth_y = store_data.GetMouthY(); + beard_color = store_data.GetBeardColor(); + beard_type = store_data.GetBeardType(); + mustache_type = store_data.GetMustacheType(); + mustache_scale = store_data.GetMustacheScale(); + mustache_y = store_data.GetMustacheY(); + glass_type = store_data.GetGlassType(); + glass_color = store_data.GetGlassColor(); + glass_scale = store_data.GetGlassScale(); + glass_y = store_data.GetGlassY(); + mole_type = store_data.GetMoleType(); + mole_scale = store_data.GetMoleScale(); + mole_x = store_data.GetMoleX(); + mole_y = store_data.GetMoleY(); + padding = '\0'; +} + +ValidationResult CharInfo::Verify() const { + if (!create_id.IsValid()) { + return ValidationResult::InvalidCreateId; + } + if (!name.IsValid()) { + return ValidationResult::InvalidName; + } + if (font_region > FontRegion::Max) { + return ValidationResult::InvalidFont; + } + if (favorite_color > FavoriteColor::Max) { + return ValidationResult::InvalidColor; + } + if (gender > Gender::Max) { + return ValidationResult::InvalidGender; + } + if (height > MaxHeight) { + return ValidationResult::InvalidHeight; + } + if (build > MaxBuild) { + return ValidationResult::InvalidBuild; + } + if (type > MaxType) { + return ValidationResult::InvalidType; + } + if (region_move > MaxRegionMove) { + return ValidationResult::InvalidRegionMove; + } + if (faceline_type > FacelineType::Max) { + return ValidationResult::InvalidFacelineType; + } + if (faceline_color > FacelineColor::Max) { + return ValidationResult::InvalidFacelineColor; + } + if (faceline_wrinkle > FacelineWrinkle::Max) { + return ValidationResult::InvalidFacelineWrinkle; + } + if (faceline_make > FacelineMake::Max) { + return ValidationResult::InvalidFacelineMake; + } + if (hair_type > HairType::Max) { + return ValidationResult::InvalidHairType; + } + if (hair_color > CommonColor::Max) { + return ValidationResult::InvalidHairColor; + } + if (hair_flip > HairFlip::Max) { + return ValidationResult::InvalidHairFlip; + } + if (eye_type > EyeType::Max) { + return ValidationResult::InvalidEyeType; + } + if (eye_color > CommonColor::Max) { + return ValidationResult::InvalidEyeColor; + } + if (eye_scale > MaxEyeScale) { + return ValidationResult::InvalidEyeScale; + } + if (eye_aspect > MaxEyeAspect) { + return ValidationResult::InvalidEyeAspect; + } + if (eye_rotate > MaxEyeX) { + return ValidationResult::InvalidEyeRotate; + } + if (eye_x > MaxEyeX) { + return ValidationResult::InvalidEyeX; + } + if (eye_y > MaxEyeY) { + return ValidationResult::InvalidEyeY; + } + if (eyebrow_type > EyebrowType::Max) { + return ValidationResult::InvalidEyebrowType; + } + if (eyebrow_color > CommonColor::Max) { + return ValidationResult::InvalidEyebrowColor; + } + if (eyebrow_scale > MaxEyebrowScale) { + return ValidationResult::InvalidEyebrowScale; + } + if (eyebrow_aspect > MaxEyebrowAspect) { + return ValidationResult::InvalidEyebrowAspect; + } + if (eyebrow_rotate > MaxEyebrowRotate) { + return ValidationResult::InvalidEyebrowRotate; + } + if (eyebrow_x > MaxEyebrowX) { + return ValidationResult::InvalidEyebrowX; + } + if (eyebrow_y > MaxEyebrowY) { + return ValidationResult::InvalidEyebrowY; + } + if (nose_type > NoseType::Max) { + return ValidationResult::InvalidNoseType; + } + if (nose_scale > MaxNoseScale) { + return ValidationResult::InvalidNoseScale; + } + if (nose_y > MaxNoseY) { + return ValidationResult::InvalidNoseY; + } + if (mouth_type > MouthType::Max) { + return ValidationResult::InvalidMouthType; + } + if (mouth_color > CommonColor::Max) { + return ValidationResult::InvalidMouthColor; + } + if (mouth_scale > MaxMouthScale) { + return ValidationResult::InvalidMouthScale; + } + if (mouth_aspect > MaxMoutAspect) { + return ValidationResult::InvalidMouthAspect; + } + if (mouth_y > MaxMouthY) { + return ValidationResult::InvalidMoleY; + } + if (beard_color > CommonColor::Max) { + return ValidationResult::InvalidBeardColor; + } + if (beard_type > BeardType::Max) { + return ValidationResult::InvalidBeardType; + } + if (mustache_type > MustacheType::Max) { + return ValidationResult::InvalidMustacheType; + } + if (mustache_scale > MaxMustacheScale) { + return ValidationResult::InvalidMustacheScale; + } + if (mustache_y > MasMustacheY) { + return ValidationResult::InvalidMustacheY; + } + if (glass_type > GlassType::Max) { + return ValidationResult::InvalidGlassType; + } + if (glass_color > CommonColor::Max) { + return ValidationResult::InvalidGlassColor; + } + if (glass_scale > MaxGlassScale) { + return ValidationResult::InvalidGlassScale; + } + if (glass_y > MaxGlassY) { + return ValidationResult::InvalidGlassY; + } + if (mole_type > MoleType::Max) { + return ValidationResult::InvalidMoleType; + } + if (mole_scale > MaxMoleScale) { + return ValidationResult::InvalidMoleScale; + } + if (mole_x > MaxMoleX) { + return ValidationResult::InvalidMoleX; + } + if (mole_y > MaxMoleY) { + return ValidationResult::InvalidMoleY; + } + return ValidationResult::NoErrors; +} + +Common::UUID CharInfo::GetCreateId() const { + return create_id; +} + +Nickname CharInfo::GetNickname() const { + return name; +} + +FontRegion CharInfo::GetFontRegion() const { + return font_region; +} + +FavoriteColor CharInfo::GetFavoriteColor() const { + return favorite_color; +} + +Gender CharInfo::GetGender() const { + return gender; +} + +u8 CharInfo::GetHeight() const { + return height; +} + +u8 CharInfo::GetBuild() const { + return build; +} + +u8 CharInfo::GetType() const { + return type; +} + +u8 CharInfo::GetRegionMove() const { + return region_move; +} + +FacelineType CharInfo::GetFacelineType() const { + return faceline_type; +} + +FacelineColor CharInfo::GetFacelineColor() const { + return faceline_color; +} + +FacelineWrinkle CharInfo::GetFacelineWrinkle() const { + return faceline_wrinkle; +} + +FacelineMake CharInfo::GetFacelineMake() const { + return faceline_make; +} + +HairType CharInfo::GetHairType() const { + return hair_type; +} + +CommonColor CharInfo::GetHairColor() const { + return hair_color; +} + +HairFlip CharInfo::GetHairFlip() const { + return hair_flip; +} + +EyeType CharInfo::GetEyeType() const { + return eye_type; +} + +CommonColor CharInfo::GetEyeColor() const { + return eye_color; +} + +u8 CharInfo::GetEyeScale() const { + return eye_scale; +} + +u8 CharInfo::GetEyeAspect() const { + return eye_aspect; +} + +u8 CharInfo::GetEyeRotate() const { + return eye_rotate; +} + +u8 CharInfo::GetEyeX() const { + return eye_x; +} + +u8 CharInfo::GetEyeY() const { + return eye_y; +} + +EyebrowType CharInfo::GetEyebrowType() const { + return eyebrow_type; +} + +CommonColor CharInfo::GetEyebrowColor() const { + return eyebrow_color; +} + +u8 CharInfo::GetEyebrowScale() const { + return eyebrow_scale; +} + +u8 CharInfo::GetEyebrowAspect() const { + return eyebrow_aspect; +} + +u8 CharInfo::GetEyebrowRotate() const { + return eyebrow_rotate; +} + +u8 CharInfo::GetEyebrowX() const { + return eyebrow_x; +} + +u8 CharInfo::GetEyebrowY() const { + return eyebrow_y; +} + +NoseType CharInfo::GetNoseType() const { + return nose_type; +} + +u8 CharInfo::GetNoseScale() const { + return nose_scale; +} + +u8 CharInfo::GetNoseY() const { + return nose_y; +} + +MouthType CharInfo::GetMouthType() const { + return mouth_type; +} + +CommonColor CharInfo::GetMouthColor() const { + return mouth_color; +} + +u8 CharInfo::GetMouthScale() const { + return mouth_scale; +} + +u8 CharInfo::GetMouthAspect() const { + return mouth_aspect; +} + +u8 CharInfo::GetMouthY() const { + return mouth_y; +} + +CommonColor CharInfo::GetBeardColor() const { + return beard_color; +} + +BeardType CharInfo::GetBeardType() const { + return beard_type; +} + +MustacheType CharInfo::GetMustacheType() const { + return mustache_type; +} + +u8 CharInfo::GetMustacheScale() const { + return mustache_scale; +} + +u8 CharInfo::GetMustacheY() const { + return mustache_y; +} + +GlassType CharInfo::GetGlassType() const { + return glass_type; +} + +CommonColor CharInfo::GetGlassColor() const { + return glass_color; +} + +u8 CharInfo::GetGlassScale() const { + return glass_scale; +} + +u8 CharInfo::GetGlassY() const { + return glass_y; +} + +MoleType CharInfo::GetMoleType() const { + return mole_type; +} + +u8 CharInfo::GetMoleScale() const { + return mole_scale; +} + +u8 CharInfo::GetMoleX() const { + return mole_x; +} + +u8 CharInfo::GetMoleY() const { + return mole_y; +} + +bool CharInfo::operator==(const CharInfo& info) { + bool is_identical = info.Verify() == ValidationResult::NoErrors; + is_identical &= name.data == info.GetNickname().data; + is_identical &= create_id == info.GetCreateId(); + is_identical &= font_region == info.GetFontRegion(); + is_identical &= favorite_color == info.GetFavoriteColor(); + is_identical &= gender == info.GetGender(); + is_identical &= height == info.GetHeight(); + is_identical &= build == info.GetBuild(); + is_identical &= type == info.GetType(); + is_identical &= region_move == info.GetRegionMove(); + is_identical &= faceline_type == info.GetFacelineType(); + is_identical &= faceline_color == info.GetFacelineColor(); + is_identical &= faceline_wrinkle == info.GetFacelineWrinkle(); + is_identical &= faceline_make == info.GetFacelineMake(); + is_identical &= hair_type == info.GetHairType(); + is_identical &= hair_color == info.GetHairColor(); + is_identical &= hair_flip == info.GetHairFlip(); + is_identical &= eye_type == info.GetEyeType(); + is_identical &= eye_color == info.GetEyeColor(); + is_identical &= eye_scale == info.GetEyeScale(); + is_identical &= eye_aspect == info.GetEyeAspect(); + is_identical &= eye_rotate == info.GetEyeRotate(); + is_identical &= eye_x == info.GetEyeX(); + is_identical &= eye_y == info.GetEyeY(); + is_identical &= eyebrow_type == info.GetEyebrowType(); + is_identical &= eyebrow_color == info.GetEyebrowColor(); + is_identical &= eyebrow_scale == info.GetEyebrowScale(); + is_identical &= eyebrow_aspect == info.GetEyebrowAspect(); + is_identical &= eyebrow_rotate == info.GetEyebrowRotate(); + is_identical &= eyebrow_x == info.GetEyebrowX(); + is_identical &= eyebrow_y == info.GetEyebrowY(); + is_identical &= nose_type == info.GetNoseType(); + is_identical &= nose_scale == info.GetNoseScale(); + is_identical &= nose_y == info.GetNoseY(); + is_identical &= mouth_type == info.GetMouthType(); + is_identical &= mouth_color == info.GetMouthColor(); + is_identical &= mouth_scale == info.GetMouthScale(); + is_identical &= mouth_aspect == info.GetMouthAspect(); + is_identical &= mouth_y == info.GetMouthY(); + is_identical &= beard_color == info.GetBeardColor(); + is_identical &= beard_type == info.GetBeardType(); + is_identical &= mustache_type == info.GetMustacheType(); + is_identical &= mustache_scale == info.GetMustacheScale(); + is_identical &= mustache_y == info.GetMustacheY(); + is_identical &= glass_type == info.GetGlassType(); + is_identical &= glass_color == info.GetGlassColor(); + is_identical &= glass_scale == info.GetGlassScale(); + is_identical &= glass_y == info.GetGlassY(); + is_identical &= mole_type == info.GetMoleType(); + is_identical &= mole_scale == info.GetMoleScale(); + is_identical &= mole_x == info.GetMoleX(); + is_identical &= mole_y == info.GetMoleY(); + return is_identical; +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h new file mode 100644 index 000000000..d069b221f --- /dev/null +++ b/src/core/hle/service/mii/types/char_info.h @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/mii/mii_types.h" + +namespace Service::Mii { +class StoreData; + +// This is nn::mii::detail::CharInfoRaw +class CharInfo { +public: + void SetFromStoreData(const StoreData& store_data_raw); + + ValidationResult Verify() const; + + Common::UUID GetCreateId() const; + Nickname GetNickname() const; + FontRegion GetFontRegion() const; + FavoriteColor GetFavoriteColor() const; + Gender GetGender() const; + u8 GetHeight() const; + u8 GetBuild() const; + u8 GetType() const; + u8 GetRegionMove() const; + FacelineType GetFacelineType() const; + FacelineColor GetFacelineColor() const; + FacelineWrinkle GetFacelineWrinkle() const; + FacelineMake GetFacelineMake() const; + HairType GetHairType() const; + CommonColor GetHairColor() const; + HairFlip GetHairFlip() const; + EyeType GetEyeType() const; + CommonColor GetEyeColor() const; + u8 GetEyeScale() const; + u8 GetEyeAspect() const; + u8 GetEyeRotate() const; + u8 GetEyeX() const; + u8 GetEyeY() const; + EyebrowType GetEyebrowType() const; + CommonColor GetEyebrowColor() const; + u8 GetEyebrowScale() const; + u8 GetEyebrowAspect() const; + u8 GetEyebrowRotate() const; + u8 GetEyebrowX() const; + u8 GetEyebrowY() const; + NoseType GetNoseType() const; + u8 GetNoseScale() const; + u8 GetNoseY() const; + MouthType GetMouthType() const; + CommonColor GetMouthColor() const; + u8 GetMouthScale() const; + u8 GetMouthAspect() const; + u8 GetMouthY() const; + CommonColor GetBeardColor() const; + BeardType GetBeardType() const; + MustacheType GetMustacheType() const; + u8 GetMustacheScale() const; + u8 GetMustacheY() const; + GlassType GetGlassType() const; + CommonColor GetGlassColor() const; + u8 GetGlassScale() const; + u8 GetGlassY() const; + MoleType GetMoleType() const; + u8 GetMoleScale() const; + u8 GetMoleX() const; + u8 GetMoleY() const; + + bool operator==(const CharInfo& info); + +private: + Common::UUID create_id; + Nickname name; + u16 null_terminator; + FontRegion font_region; + FavoriteColor favorite_color; + Gender gender; + u8 height; + u8 build; + u8 type; + u8 region_move; + FacelineType faceline_type; + FacelineColor faceline_color; + FacelineWrinkle faceline_wrinkle; + FacelineMake faceline_make; + HairType hair_type; + CommonColor hair_color; + HairFlip hair_flip; + EyeType eye_type; + CommonColor eye_color; + u8 eye_scale; + u8 eye_aspect; + u8 eye_rotate; + u8 eye_x; + u8 eye_y; + EyebrowType eyebrow_type; + CommonColor eyebrow_color; + u8 eyebrow_scale; + u8 eyebrow_aspect; + u8 eyebrow_rotate; + u8 eyebrow_x; + u8 eyebrow_y; + NoseType nose_type; + u8 nose_scale; + u8 nose_y; + MouthType mouth_type; + CommonColor mouth_color; + u8 mouth_scale; + u8 mouth_aspect; + u8 mouth_y; + CommonColor beard_color; + BeardType beard_type; + MustacheType mustache_type; + u8 mustache_scale; + u8 mustache_y; + GlassType glass_type; + CommonColor glass_color; + u8 glass_scale; + u8 glass_y; + MoleType mole_type; + u8 mole_scale; + u8 mole_x; + u8 mole_y; + u8 padding; +}; +static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); +static_assert(std::has_unique_object_representations_v<CharInfo>, + "All bits of CharInfo must contribute to its value."); + +struct CharInfoElement { + CharInfo char_info{}; + Source source{}; +}; +static_assert(sizeof(CharInfoElement) == 0x5c, "CharInfoElement has incorrect size."); + +}; // namespace Service::Mii diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp new file mode 100644 index 000000000..659288b51 --- /dev/null +++ b/src/core/hle/service/mii/types/core_data.cpp @@ -0,0 +1,601 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/hle/service/mii/mii_util.h" +#include "core/hle/service/mii/types/core_data.h" +#include "core/hle/service/mii/types/raw_data.h" + +namespace Service::Mii { + +void CoreData::SetDefault() { + data = {}; + name = GetDefaultNickname(); +} + +void CoreData::BuildRandom(Age age, Gender gender, Race race) { + if (gender == Gender::All) { + gender = MiiUtil::GetRandomValue(Gender::Max); + } + + if (age == Age::All) { + const auto random{MiiUtil::GetRandomValue<int>(10)}; + if (random >= 8) { + age = Age::Old; + } else if (random >= 4) { + age = Age::Normal; + } else { + age = Age::Young; + } + } + + if (race == Race::All) { + const auto random{MiiUtil::GetRandomValue<int>(10)}; + if (random >= 8) { + race = Race::Black; + } else if (random >= 4) { + race = Race::White; + } else { + race = Race::Asian; + } + } + + SetGender(gender); + SetFavoriteColor(MiiUtil::GetRandomValue(FavoriteColor::Max)); + SetRegionMove(0); + SetFontRegion(FontRegion::Standard); + SetType(0); + SetHeight(64); + SetBuild(64); + + u32 axis_y{}; + if (gender == Gender::Female && age == Age::Young) { + axis_y = MiiUtil::GetRandomValue<u32>(3); + } + + const std::size_t index{3 * static_cast<std::size_t>(age) + + 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)}; + + const auto& faceline_type_info{RawData::RandomMiiFaceline.at(index)}; + const auto& faceline_color_info{RawData::RandomMiiFacelineColor.at( + 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))}; + const auto& faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)}; + const auto& faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)}; + const auto& hair_type_info{RawData::RandomMiiHairType.at(index)}; + const auto& hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) + + static_cast<std::size_t>(age))}; + const auto& eye_type_info{RawData::RandomMiiEyeType.at(index)}; + const auto& eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))}; + const auto& eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)}; + const auto& nose_type_info{RawData::RandomMiiNoseType.at(index)}; + const auto& mouth_type_info{RawData::RandomMiiMouthType.at(index)}; + const auto& glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))}; + + data.faceline_type.Assign( + faceline_type_info + .values[MiiUtil::GetRandomValue<std::size_t>(faceline_type_info.values_count)]); + data.faceline_color.Assign( + faceline_color_info + .values[MiiUtil::GetRandomValue<std::size_t>(faceline_color_info.values_count)]); + data.faceline_wrinkle.Assign( + faceline_wrinkle_info + .values[MiiUtil::GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]); + data.faceline_makeup.Assign( + faceline_makeup_info + .values[MiiUtil::GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]); + + data.hair_type.Assign( + hair_type_info.values[MiiUtil::GetRandomValue<std::size_t>(hair_type_info.values_count)]); + SetHairColor(RawData::GetHairColorFromVer3( + hair_color_info + .values[MiiUtil::GetRandomValue<std::size_t>(hair_color_info.values_count)])); + SetHairFlip(MiiUtil::GetRandomValue(HairFlip::Max)); + + data.eye_type.Assign( + eye_type_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_type_info.values_count)]); + + const auto eye_rotate_1{gender != Gender::Male ? 4 : 2}; + const auto eye_rotate_2{gender != Gender::Male ? 3 : 4}; + const auto eye_rotate_offset{32 - RawData::EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; + const auto eye_rotate{32 - RawData::EyeRotateLookup[data.eye_type]}; + + SetEyeColor(RawData::GetEyeColorFromVer3( + eye_color_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_color_info.values_count)])); + SetEyeScale(4); + SetEyeAspect(3); + SetEyeRotate(static_cast<u8>(eye_rotate_offset - eye_rotate)); + SetEyeX(2); + SetEyeY(static_cast<u8>(axis_y + 12)); + + data.eyebrow_type.Assign( + eyebrow_type_info + .values[MiiUtil::GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]); + + const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0}; + const auto eyebrow_y{race == Race::Asian ? 9 : 10}; + const auto eyebrow_rotate_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6}; + const auto eyebrow_rotate{ + 32 - RawData::EyebrowRotateLookup[static_cast<std::size_t>(data.eyebrow_type.Value())]}; + + SetEyebrowColor(GetHairColor()); + SetEyebrowScale(4); + SetEyebrowAspect(3); + SetEyebrowRotate(static_cast<u8>(eyebrow_rotate_offset - eyebrow_rotate)); + SetEyebrowX(2); + SetEyebrowY(static_cast<u8>(axis_y + eyebrow_y)); + + data.nose_type.Assign( + nose_type_info.values[MiiUtil::GetRandomValue<std::size_t>(nose_type_info.values_count)]); + SetNoseScale(gender == Gender::Female ? 3 : 4); + SetNoseY(static_cast<u8>(axis_y + 9)); + + const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue<int>(4) : 0}; + + data.mouth_type.Assign( + mouth_type_info.values[MiiUtil::GetRandomValue<std::size_t>(mouth_type_info.values_count)]); + SetMouthColor(RawData::GetMouthColorFromVer3(mouth_color)); + SetMouthScale(4); + SetMouthAspect(3); + SetMouthY(static_cast<u8>(axis_y + 13)); + + SetBeardColor(GetHairColor()); + SetMustacheScale(4); + + if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue<int>(10) < 2) { + const auto mustache_and_beard_flag{MiiUtil::GetRandomValue(BeardAndMustacheFlag::All)}; + + auto beard_type{BeardType::None}; + auto mustache_type{MustacheType::None}; + + if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) == + BeardAndMustacheFlag::Beard) { + beard_type = MiiUtil::GetRandomValue(BeardType::Min, BeardType::Max); + } + + if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == + BeardAndMustacheFlag::Mustache) { + mustache_type = MiiUtil::GetRandomValue(MustacheType::Min, MustacheType::Max); + } + + SetMustacheType(mustache_type); + SetBeardType(beard_type); + SetMustacheY(10); + } else { + SetMustacheType(MustacheType::None); + SetBeardType(BeardType::None); + SetMustacheY(static_cast<u8>(axis_y + 10)); + } + + const auto glasses_type_start{MiiUtil::GetRandomValue<std::size_t>(100)}; + u8 glasses_type{}; + while (glasses_type_start < glasses_type_info.values[glasses_type]) { + if (++glasses_type >= glasses_type_info.values_count) { + ASSERT(false); + break; + } + } + + SetGlassType(static_cast<GlassType>(glasses_type)); + SetGlassColor(RawData::GetGlassColorFromVer3(0)); + SetGlassScale(4); + + SetMoleType(MoleType::None); + SetMoleScale(4); + SetMoleX(2); + SetMoleY(20); +} + +u32 CoreData::IsValid() const { + // TODO: Complete this + return 0; +} + +void CoreData::SetFontRegion(FontRegion value) { + data.font_region.Assign(static_cast<u32>(value)); +} + +void CoreData::SetFavoriteColor(FavoriteColor value) { + data.favorite_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetGender(Gender value) { + data.gender.Assign(static_cast<u32>(value)); +} + +void CoreData::SetHeight(u8 value) { + data.height.Assign(value); +} + +void CoreData::SetBuild(u8 value) { + data.build.Assign(value); +} + +void CoreData::SetType(u8 value) { + data.type.Assign(value); +} + +void CoreData::SetRegionMove(u8 value) { + data.region_move.Assign(value); +} + +void CoreData::SetFacelineType(FacelineType value) { + data.faceline_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetFacelineColor(FacelineColor value) { + data.faceline_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetFacelineWrinkle(FacelineWrinkle value) { + data.faceline_wrinkle.Assign(static_cast<u32>(value)); +} + +void CoreData::SetFacelineMake(FacelineMake value) { + data.faceline_makeup.Assign(static_cast<u32>(value)); +} + +void CoreData::SetHairType(HairType value) { + data.hair_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetHairColor(CommonColor value) { + data.hair_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetHairFlip(HairFlip value) { + data.hair_flip.Assign(static_cast<u32>(value)); +} + +void CoreData::SetEyeType(EyeType value) { + data.eye_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetEyeColor(CommonColor value) { + data.eye_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetEyeScale(u8 value) { + data.eye_scale.Assign(value); +} + +void CoreData::SetEyeAspect(u8 value) { + data.eye_aspect.Assign(value); +} + +void CoreData::SetEyeRotate(u8 value) { + data.eye_rotate.Assign(value); +} + +void CoreData::SetEyeX(u8 value) { + data.eye_x.Assign(value); +} + +void CoreData::SetEyeY(u8 value) { + data.eye_y.Assign(value); +} + +void CoreData::SetEyebrowType(EyebrowType value) { + data.eyebrow_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetEyebrowColor(CommonColor value) { + data.eyebrow_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetEyebrowScale(u8 value) { + data.eyebrow_scale.Assign(value); +} + +void CoreData::SetEyebrowAspect(u8 value) { + data.eyebrow_aspect.Assign(value); +} + +void CoreData::SetEyebrowRotate(u8 value) { + data.eyebrow_rotate.Assign(value); +} + +void CoreData::SetEyebrowX(u8 value) { + data.eyebrow_x.Assign(value); +} + +void CoreData::SetEyebrowY(u8 value) { + data.eyebrow_y.Assign(value); +} + +void CoreData::SetNoseType(NoseType value) { + data.nose_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetNoseScale(u8 value) { + data.nose_scale.Assign(value); +} + +void CoreData::SetNoseY(u8 value) { + data.nose_y.Assign(value); +} + +void CoreData::SetMouthType(u8 value) { + data.mouth_type.Assign(value); +} + +void CoreData::SetMouthColor(CommonColor value) { + data.mouth_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetMouthScale(u8 value) { + data.mouth_scale.Assign(value); +} + +void CoreData::SetMouthAspect(u8 value) { + data.mouth_aspect.Assign(value); +} + +void CoreData::SetMouthY(u8 value) { + data.mouth_y.Assign(value); +} + +void CoreData::SetBeardColor(CommonColor value) { + data.beard_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetBeardType(BeardType value) { + data.beard_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetMustacheType(MustacheType value) { + data.mustache_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetMustacheScale(u8 value) { + data.mustache_scale.Assign(value); +} + +void CoreData::SetMustacheY(u8 value) { + data.mustache_y.Assign(value); +} + +void CoreData::SetGlassType(GlassType value) { + data.glasses_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetGlassColor(CommonColor value) { + data.glasses_color.Assign(static_cast<u32>(value)); +} + +void CoreData::SetGlassScale(u8 value) { + data.glasses_scale.Assign(value); +} + +void CoreData::SetGlassY(u8 value) { + data.glasses_y.Assign(value); +} + +void CoreData::SetMoleType(MoleType value) { + data.mole_type.Assign(static_cast<u32>(value)); +} + +void CoreData::SetMoleScale(u8 value) { + data.mole_scale.Assign(value); +} + +void CoreData::SetMoleX(u8 value) { + data.mole_x.Assign(value); +} + +void CoreData::SetMoleY(u8 value) { + data.mole_y.Assign(value); +} + +void CoreData::SetNickname(Nickname nickname) { + name = nickname; +} + +FontRegion CoreData::GetFontRegion() const { + return static_cast<FontRegion>(data.font_region.Value()); +} + +FavoriteColor CoreData::GetFavoriteColor() const { + return static_cast<FavoriteColor>(data.favorite_color.Value()); +} + +Gender CoreData::GetGender() const { + return static_cast<Gender>(data.gender.Value()); +} + +u8 CoreData::GetHeight() const { + return static_cast<u8>(data.height.Value()); +} + +u8 CoreData::GetBuild() const { + return static_cast<u8>(data.build.Value()); +} + +u8 CoreData::GetType() const { + return static_cast<u8>(data.type.Value()); +} + +u8 CoreData::GetRegionMove() const { + return static_cast<u8>(data.region_move.Value()); +} + +FacelineType CoreData::GetFacelineType() const { + return static_cast<FacelineType>(data.faceline_type.Value()); +} + +FacelineColor CoreData::GetFacelineColor() const { + return static_cast<FacelineColor>(data.faceline_color.Value()); +} + +FacelineWrinkle CoreData::GetFacelineWrinkle() const { + return static_cast<FacelineWrinkle>(data.faceline_wrinkle.Value()); +} + +FacelineMake CoreData::GetFacelineMake() const { + return static_cast<FacelineMake>(data.faceline_makeup.Value()); +} + +HairType CoreData::GetHairType() const { + return static_cast<HairType>(data.hair_type.Value()); +} + +CommonColor CoreData::GetHairColor() const { + return static_cast<CommonColor>(data.hair_color.Value()); +} + +HairFlip CoreData::GetHairFlip() const { + return static_cast<HairFlip>(data.hair_flip.Value()); +} + +EyeType CoreData::GetEyeType() const { + return static_cast<EyeType>(data.eye_type.Value()); +} + +CommonColor CoreData::GetEyeColor() const { + return static_cast<CommonColor>(data.eye_color.Value()); +} + +u8 CoreData::GetEyeScale() const { + return static_cast<u8>(data.eye_scale.Value()); +} + +u8 CoreData::GetEyeAspect() const { + return static_cast<u8>(data.eye_aspect.Value()); +} + +u8 CoreData::GetEyeRotate() const { + return static_cast<u8>(data.eye_rotate.Value()); +} + +u8 CoreData::GetEyeX() const { + return static_cast<u8>(data.eye_x.Value()); +} + +u8 CoreData::GetEyeY() const { + return static_cast<u8>(data.eye_y.Value()); +} + +EyebrowType CoreData::GetEyebrowType() const { + return static_cast<EyebrowType>(data.eyebrow_type.Value()); +} + +CommonColor CoreData::GetEyebrowColor() const { + return static_cast<CommonColor>(data.eyebrow_color.Value()); +} + +u8 CoreData::GetEyebrowScale() const { + return static_cast<u8>(data.eyebrow_scale.Value()); +} + +u8 CoreData::GetEyebrowAspect() const { + return static_cast<u8>(data.eyebrow_aspect.Value()); +} + +u8 CoreData::GetEyebrowRotate() const { + return static_cast<u8>(data.eyebrow_rotate.Value()); +} + +u8 CoreData::GetEyebrowX() const { + return static_cast<u8>(data.eyebrow_x.Value()); +} + +u8 CoreData::GetEyebrowY() const { + return static_cast<u8>(data.eyebrow_y.Value()); +} + +NoseType CoreData::GetNoseType() const { + return static_cast<NoseType>(data.nose_type.Value()); +} + +u8 CoreData::GetNoseScale() const { + return static_cast<u8>(data.nose_scale.Value()); +} + +u8 CoreData::GetNoseY() const { + return static_cast<u8>(data.nose_y.Value()); +} + +MouthType CoreData::GetMouthType() const { + return static_cast<MouthType>(data.mouth_type.Value()); +} + +CommonColor CoreData::GetMouthColor() const { + return static_cast<CommonColor>(data.mouth_color.Value()); +} + +u8 CoreData::GetMouthScale() const { + return static_cast<u8>(data.mouth_scale.Value()); +} + +u8 CoreData::GetMouthAspect() const { + return static_cast<u8>(data.mouth_aspect.Value()); +} + +u8 CoreData::GetMouthY() const { + return static_cast<u8>(data.mouth_y.Value()); +} + +CommonColor CoreData::GetBeardColor() const { + return static_cast<CommonColor>(data.beard_color.Value()); +} + +BeardType CoreData::GetBeardType() const { + return static_cast<BeardType>(data.beard_type.Value()); +} + +MustacheType CoreData::GetMustacheType() const { + return static_cast<MustacheType>(data.mustache_type.Value()); +} + +u8 CoreData::GetMustacheScale() const { + return static_cast<u8>(data.mustache_scale.Value()); +} + +u8 CoreData::GetMustacheY() const { + return static_cast<u8>(data.mustache_y.Value()); +} + +GlassType CoreData::GetGlassType() const { + return static_cast<GlassType>(data.glasses_type.Value()); +} + +CommonColor CoreData::GetGlassColor() const { + return static_cast<CommonColor>(data.glasses_color.Value()); +} + +u8 CoreData::GetGlassScale() const { + return static_cast<u8>(data.glasses_scale.Value()); +} + +u8 CoreData::GetGlassY() const { + return static_cast<u8>(data.glasses_y.Value()); +} + +MoleType CoreData::GetMoleType() const { + return static_cast<MoleType>(data.mole_type.Value()); +} + +u8 CoreData::GetMoleScale() const { + return static_cast<u8>(data.mole_scale.Value()); +} + +u8 CoreData::GetMoleX() const { + return static_cast<u8>(data.mole_x.Value()); +} + +u8 CoreData::GetMoleY() const { + return static_cast<u8>(data.mole_y.Value()); +} + +Nickname CoreData::GetNickname() const { + return name; +} + +Nickname CoreData::GetDefaultNickname() const { + return {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}; +} + +Nickname CoreData::GetInvalidNickname() const { + return {u'?', u'?', u'?'}; +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/types/core_data.h b/src/core/hle/service/mii/types/core_data.h new file mode 100644 index 000000000..cebcd2ee4 --- /dev/null +++ b/src/core/hle/service/mii/types/core_data.h @@ -0,0 +1,216 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/mii/mii_types.h" + +namespace Service::Mii { + +struct StoreDataBitFields { + union { + u32 word_0{}; + + BitField<0, 8, u32> hair_type; + BitField<8, 7, u32> height; + BitField<15, 1, u32> mole_type; + BitField<16, 7, u32> build; + BitField<23, 1, u32> hair_flip; + BitField<24, 7, u32> hair_color; + BitField<31, 1, u32> type; + }; + + union { + u32 word_1{}; + + BitField<0, 7, u32> eye_color; + BitField<7, 1, u32> gender; + BitField<8, 7, u32> eyebrow_color; + BitField<16, 7, u32> mouth_color; + BitField<24, 7, u32> beard_color; + }; + + union { + u32 word_2{}; + + BitField<0, 7, u32> glasses_color; + BitField<8, 6, u32> eye_type; + BitField<14, 2, u32> region_move; + BitField<16, 6, u32> mouth_type; + BitField<22, 2, u32> font_region; + BitField<24, 5, u32> eye_y; + BitField<29, 3, u32> glasses_scale; + }; + + union { + u32 word_3{}; + + BitField<0, 5, u32> eyebrow_type; + BitField<5, 3, u32> mustache_type; + BitField<8, 5, u32> nose_type; + BitField<13, 3, u32> beard_type; + BitField<16, 5, u32> nose_y; + BitField<21, 3, u32> mouth_aspect; + BitField<24, 5, u32> mouth_y; + BitField<29, 3, u32> eyebrow_aspect; + }; + + union { + u32 word_4{}; + + BitField<0, 5, u32> mustache_y; + BitField<5, 3, u32> eye_rotate; + BitField<8, 5, u32> glasses_y; + BitField<13, 3, u32> eye_aspect; + BitField<16, 5, u32> mole_x; + BitField<21, 3, u32> eye_scale; + BitField<24, 5, u32> mole_y; + }; + + union { + u32 word_5{}; + + BitField<0, 5, u32> glasses_type; + BitField<8, 4, u32> favorite_color; + BitField<12, 4, u32> faceline_type; + BitField<16, 4, u32> faceline_color; + BitField<20, 4, u32> faceline_wrinkle; + BitField<24, 4, u32> faceline_makeup; + BitField<28, 4, u32> eye_x; + }; + + union { + u32 word_6{}; + + BitField<0, 4, u32> eyebrow_scale; + BitField<4, 4, u32> eyebrow_rotate; + BitField<8, 4, u32> eyebrow_x; + BitField<12, 4, u32> eyebrow_y; + BitField<16, 4, u32> nose_scale; + BitField<20, 4, u32> mouth_scale; + BitField<24, 4, u32> mustache_scale; + BitField<28, 4, u32> mole_scale; + }; +}; +static_assert(sizeof(StoreDataBitFields) == 0x1c, "StoreDataBitFields has incorrect size."); +static_assert(std::is_trivially_copyable_v<StoreDataBitFields>, + "StoreDataBitFields is not trivially copyable."); + +class CoreData { +public: + void SetDefault(); + void BuildRandom(Age age, Gender gender, Race race); + + u32 IsValid() const; + + void SetFontRegion(FontRegion value); + void SetFavoriteColor(FavoriteColor value); + void SetGender(Gender value); + void SetHeight(u8 value); + void SetBuild(u8 value); + void SetType(u8 value); + void SetRegionMove(u8 value); + void SetFacelineType(FacelineType value); + void SetFacelineColor(FacelineColor value); + void SetFacelineWrinkle(FacelineWrinkle value); + void SetFacelineMake(FacelineMake value); + void SetHairType(HairType value); + void SetHairColor(CommonColor value); + void SetHairFlip(HairFlip value); + void SetEyeType(EyeType value); + void SetEyeColor(CommonColor value); + void SetEyeScale(u8 value); + void SetEyeAspect(u8 value); + void SetEyeRotate(u8 value); + void SetEyeX(u8 value); + void SetEyeY(u8 value); + void SetEyebrowType(EyebrowType value); + void SetEyebrowColor(CommonColor value); + void SetEyebrowScale(u8 value); + void SetEyebrowAspect(u8 value); + void SetEyebrowRotate(u8 value); + void SetEyebrowX(u8 value); + void SetEyebrowY(u8 value); + void SetNoseType(NoseType value); + void SetNoseScale(u8 value); + void SetNoseY(u8 value); + void SetMouthType(u8 value); + void SetMouthColor(CommonColor value); + void SetMouthScale(u8 value); + void SetMouthAspect(u8 value); + void SetMouthY(u8 value); + void SetBeardColor(CommonColor value); + void SetBeardType(BeardType value); + void SetMustacheType(MustacheType value); + void SetMustacheScale(u8 value); + void SetMustacheY(u8 value); + void SetGlassType(GlassType value); + void SetGlassColor(CommonColor value); + void SetGlassScale(u8 value); + void SetGlassY(u8 value); + void SetMoleType(MoleType value); + void SetMoleScale(u8 value); + void SetMoleX(u8 value); + void SetMoleY(u8 value); + void SetNickname(Nickname nickname); + + FontRegion GetFontRegion() const; + FavoriteColor GetFavoriteColor() const; + Gender GetGender() const; + u8 GetHeight() const; + u8 GetBuild() const; + u8 GetType() const; + u8 GetRegionMove() const; + FacelineType GetFacelineType() const; + FacelineColor GetFacelineColor() const; + FacelineWrinkle GetFacelineWrinkle() const; + FacelineMake GetFacelineMake() const; + HairType GetHairType() const; + CommonColor GetHairColor() const; + HairFlip GetHairFlip() const; + EyeType GetEyeType() const; + CommonColor GetEyeColor() const; + u8 GetEyeScale() const; + u8 GetEyeAspect() const; + u8 GetEyeRotate() const; + u8 GetEyeX() const; + u8 GetEyeY() const; + EyebrowType GetEyebrowType() const; + CommonColor GetEyebrowColor() const; + u8 GetEyebrowScale() const; + u8 GetEyebrowAspect() const; + u8 GetEyebrowRotate() const; + u8 GetEyebrowX() const; + u8 GetEyebrowY() const; + NoseType GetNoseType() const; + u8 GetNoseScale() const; + u8 GetNoseY() const; + MouthType GetMouthType() const; + CommonColor GetMouthColor() const; + u8 GetMouthScale() const; + u8 GetMouthAspect() const; + u8 GetMouthY() const; + CommonColor GetBeardColor() const; + BeardType GetBeardType() const; + MustacheType GetMustacheType() const; + u8 GetMustacheScale() const; + u8 GetMustacheY() const; + GlassType GetGlassType() const; + CommonColor GetGlassColor() const; + u8 GetGlassScale() const; + u8 GetGlassY() const; + MoleType GetMoleType() const; + u8 GetMoleScale() const; + u8 GetMoleX() const; + u8 GetMoleY() const; + Nickname GetNickname() const; + Nickname GetDefaultNickname() const; + Nickname GetInvalidNickname() const; + +private: + StoreDataBitFields data{}; + Nickname name{}; +}; +static_assert(sizeof(CoreData) == 0x30, "CoreData has incorrect size."); + +}; // namespace Service::Mii diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/types/raw_data.cpp index 1442280c8..5143abcc8 100644 --- a/src/core/hle/service/mii/raw_data.cpp +++ b/src/core/hle/service/mii/types/raw_data.cpp @@ -1,11 +1,88 @@ // 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 { -const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ +constexpr std::array<u8, static_cast<u8>(FacelineColor::Count)> FromVer3FacelineColorTable{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5, +}; + +constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3HairColorTable{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0, + 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, + 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4, + 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, + 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7, + 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4, +}; + +constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3EyeColorTable{ + 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4, + 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, + 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4, + 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, + 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, + 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, +}; + +constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3MouthlineColorTable{ + 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, + 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4, + 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, + 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, + 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, + 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3, +}; + +constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3GlassColorTable{ + 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, + 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, + 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5, + 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4, + 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, +}; + +constexpr std::array<u8, static_cast<u8>(GlassType::Count)> FromVer3GlassTypeTable{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, + 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7, +}; + +constexpr std::array<u8, 8> Ver3FacelineColorTable{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, +}; + +constexpr std::array<u8, 8> Ver3HairColorTable{ + 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, +}; + +constexpr std::array<u8, 6> Ver3EyeColorTable{ + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, +}; + +constexpr std::array<u8, 5> Ver3MouthColorTable{ + 0x13, 0x14, 0x15, 0x16, 0x17, +}; + +constexpr std::array<u8, 7> Ver3GlassColorTable{ + 0x8, 0xe, 0xf, 0x10, 0x11, 0x12, 0x0, +}; + +const std::array<u8, 62> EyeRotateLookup{ + 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, +}; + +const std::array<u8, 24> EyebrowRotateLookup{ + 0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x06, 0x08, + 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05, +}; + +const std::array<Service::Mii::DefaultMii, 2> BaseMii{ Service::Mii::DefaultMii{ .face_type = 0, .face_color = 0, @@ -51,11 +128,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 0, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -102,12 +180,16 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 0, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, +}; + +const std::array<Service::Mii::DefaultMii, 6> DefaultMii{ Service::Mii::DefaultMii{ .face_type = 0, .face_color = 4, @@ -153,11 +235,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 4, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -204,11 +287,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 5, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -255,11 +339,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Male, + .gender = 0, .favorite_color = 0, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -306,11 +391,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 2, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -357,11 +443,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 6, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, Service::Mii::DefaultMii{ .face_type = 0, @@ -408,176 +495,177 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ .mole_y = 20, .height = 64, .weight = 64, - .gender = Gender::Female, + .gender = 1, .favorite_color = 7, - .region = 0, - .font_region = FontRegion::Standard, + .region_move = 0, + .font_region = 0, .type = 0, + .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'}, }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiFaceline{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 13, .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 13, .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 12, .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 10, .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 12, .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, }, }; -const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{ - Service::Mii::RandomMiiData3{ +const std::array<RandomMiiData3, 6> RandomMiiFacelineColor{ + RandomMiiData3{ .arg_1 = 0, .arg_2 = 0, .values_count = 10, .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 0, .arg_2 = 1, .values_count = 10, .values = {0, 0, 0, 0, 1, 1, 2, 3, 3, 3}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 0, .arg_2 = 2, .values_count = 10, .values = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 1, .arg_2 = 0, .values_count = 10, .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 1, .arg_2 = 1, .values_count = 10, .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 3}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 1, .arg_2 = 2, .values_count = 10, @@ -585,407 +673,407 @@ const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{ }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiHairType{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 30, .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 75, 76, 86, 89}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 31, .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 31, .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 38, .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 40, 42, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 75, 76, 86, 89}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 39, .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 39, .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 18, .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 19, .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 19, .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 39, .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 76, 77, 79, 80, 83, 85}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 42, .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 42, .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 44, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 42, 50, 58, 60, 62, 63, 64, 69, 71, 76, 79, 80, 81, 82, 83, 86}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 44, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 44, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 24, .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 76, 83}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 27, .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 27, .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, @@ -993,55 +1081,55 @@ const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{ }; const std::array<RandomMiiData3, 9> RandomMiiHairColor{ - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 0, .arg_2 = 0, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 0, .arg_2 = 1, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 0, .arg_2 = 2, .values_count = 20, .values = {0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 1, .arg_2 = 0, .values_count = 20, .values = {2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 1, .arg_2 = 1, .values_count = 20, .values = {2, 3, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 1, .arg_2 = 2, .values_count = 20, .values = {2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 2, .arg_2 = 0, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 2, .arg_2 = 1, .values_count = 20, .values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3}, }, - Service::Mii::RandomMiiData3{ + RandomMiiData3{ .arg_1 = 2, .arg_2 = 2, .values_count = 20, @@ -1049,598 +1137,642 @@ const std::array<RandomMiiData3, 9> RandomMiiHairColor{ }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiEyeType{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 26, .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 26, .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 27, .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 26, 27, 29, 32, 34, 36, 38, 39, 41, 43, 47, 48, 49, 53, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 35, .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 35, .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 35, .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 21, 22, 26, 27, 29, 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 48, 49, 50, 53, 56, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 30, .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 30, .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 30, .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 22, 26, 31, 32, 34, 36, 37, 39, 41, 44, 48, 49, 50, 51, 53, 57}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 39, .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 39, .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 40, .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 46, .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 46, .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 46, .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 34, .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 34, .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 35, .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, }, }; -const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor{ - Service::Mii::RandomMiiData2{ +const std::array<RandomMiiData2, 3> RandomMiiEyeColor{ + RandomMiiData2{ .arg_1 = 0, .values_count = 10, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, }, - Service::Mii::RandomMiiData2{ + RandomMiiData2{ .arg_1 = 1, .values_count = 10, .values = {0, 1, 1, 2, 3, 3, 4, 4, 4, 5}, }, - Service::Mii::RandomMiiData2{ + RandomMiiData2{ .arg_1 = 2, .values_count = 10, .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiEyebrowType{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 23, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 23, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 23, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 21, .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 21, .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 21, .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 9, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 11, .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 9, .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiNoseType{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 11, .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 15, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 18, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 15, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 8, .values = {0, 1, 3, 4, 8, 10, 13, 14}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 12, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 11, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 10, .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 12, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 11, .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 10, .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, }, }; -const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType{ - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Black, +const std::array<RandomMiiData4, 18> RandomMiiMouthType{ + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 25, .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 27, .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 28, .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 28, 30, 31, 32, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 24, .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 26, .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 26, .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 24, .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 26, .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Male, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Male), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 26, .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Black), .values_count = 25, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Black), .values_count = 26, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Black, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Black), .values_count = 26, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::White), .values_count = 25, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::White), .values_count = 26, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::White, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::White), .values_count = 25, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 24, 25, 29, 33, 35}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Young, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Young), + .race = static_cast<u32>(Race::Asian), .values_count = 24, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Normal, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Normal), + .race = static_cast<u32>(Race::Asian), .values_count = 25, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, }, - Service::Mii::RandomMiiData4{ - .gender = Gender::Female, - .age = Age::Old, - .race = Race::Asian, + RandomMiiData4{ + .gender = static_cast<u32>(Gender::Female), + .age = static_cast<u32>(Age::Old), + .race = static_cast<u32>(Race::Asian), .values_count = 25, .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, }, }; -const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType{ - Service::Mii::RandomMiiData2{ +const std::array<RandomMiiData2, 3> RandomMiiGlassType{ + RandomMiiData2{ .arg_1 = 0, .values_count = 9, .values = {90, 94, 96, 100, 0, 0, 0, 0, 0}, }, - Service::Mii::RandomMiiData2{ + RandomMiiData2{ .arg_1 = 1, .values_count = 9, .values = {83, 86, 90, 93, 94, 96, 98, 100, 0}, }, - Service::Mii::RandomMiiData2{ + RandomMiiData2{ .arg_1 = 2, .values_count = 9, .values = {78, 83, 0, 93, 0, 0, 98, 100, 0}, }, }; +u8 FromVer3GetFacelineColor(u8 color) { + return FromVer3FacelineColorTable[color]; +} + +u8 FromVer3GetHairColor(u8 color) { + return FromVer3HairColorTable[color]; +} + +u8 FromVer3GetEyeColor(u8 color) { + return FromVer3EyeColorTable[color]; +} + +u8 FromVer3GetMouthlineColor(u8 color) { + return FromVer3MouthlineColorTable[color]; +} + +u8 FromVer3GetGlassColor(u8 color) { + return FromVer3GlassColorTable[color]; +} + +u8 FromVer3GetGlassType(u8 type) { + return FromVer3GlassTypeTable[type]; +} + +FacelineColor GetFacelineColorFromVer3(u32 color) { + return static_cast<FacelineColor>(Ver3FacelineColorTable[color]); +} + +CommonColor GetHairColorFromVer3(u32 color) { + return static_cast<CommonColor>(Ver3HairColorTable[color]); +} + +CommonColor GetEyeColorFromVer3(u32 color) { + return static_cast<CommonColor>(Ver3EyeColorTable[color]); +} + +CommonColor GetMouthColorFromVer3(u32 color) { + return static_cast<CommonColor>(Ver3MouthColorTable[color]); +} + +CommonColor GetGlassColorFromVer3(u32 color) { + return static_cast<CommonColor>(Ver3GlassColorTable[color]); +} + } // namespace Service::Mii::RawData diff --git a/src/core/hle/service/mii/types/raw_data.h b/src/core/hle/service/mii/types/raw_data.h new file mode 100644 index 000000000..9a4cfa738 --- /dev/null +++ b/src/core/hle/service/mii/types/raw_data.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "core/hle/service/mii/mii_types.h" + +namespace Service::Mii::RawData { + +struct RandomMiiValues { + std::array<u8, 188> values{}; +}; +static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); + +struct RandomMiiData4 { + u32 gender{}; + u32 age{}; + u32 race{}; + u32 values_count{}; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); + +struct RandomMiiData3 { + u32 arg_1; + u32 arg_2; + u32 values_count; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); + +struct RandomMiiData2 { + u32 arg_1; + u32 values_count; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); + +extern const std::array<Service::Mii::DefaultMii, 2> BaseMii; +extern const std::array<Service::Mii::DefaultMii, 6> DefaultMii; + +extern const std::array<u8, 62> EyeRotateLookup; +extern const std::array<u8, 24> EyebrowRotateLookup; + +extern const std::array<RandomMiiData4, 18> RandomMiiFaceline; +extern const std::array<RandomMiiData3, 6> RandomMiiFacelineColor; +extern const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle; +extern const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup; +extern const std::array<RandomMiiData4, 18> RandomMiiHairType; +extern const std::array<RandomMiiData3, 9> RandomMiiHairColor; +extern const std::array<RandomMiiData4, 18> RandomMiiEyeType; +extern const std::array<RandomMiiData2, 3> RandomMiiEyeColor; +extern const std::array<RandomMiiData4, 18> RandomMiiEyebrowType; +extern const std::array<RandomMiiData4, 18> RandomMiiNoseType; +extern const std::array<RandomMiiData4, 18> RandomMiiMouthType; +extern const std::array<RandomMiiData2, 3> RandomMiiGlassType; + +u8 FromVer3GetFacelineColor(u8 color); +u8 FromVer3GetHairColor(u8 color); +u8 FromVer3GetEyeColor(u8 color); +u8 FromVer3GetMouthlineColor(u8 color); +u8 FromVer3GetGlassColor(u8 color); +u8 FromVer3GetGlassType(u8 type); + +FacelineColor GetFacelineColorFromVer3(u32 color); +CommonColor GetHairColorFromVer3(u32 color); +CommonColor GetEyeColorFromVer3(u32 color); +CommonColor GetMouthColorFromVer3(u32 color); +CommonColor GetGlassColorFromVer3(u32 color); + +} // namespace Service::Mii::RawData diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp new file mode 100644 index 000000000..8fce636c7 --- /dev/null +++ b/src/core/hle/service/mii/types/store_data.cpp @@ -0,0 +1,643 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/mii/mii_util.h" +#include "core/hle/service/mii/types/raw_data.h" +#include "core/hle/service/mii/types/store_data.h" + +namespace Service::Mii { + +void StoreData::BuildDefault(u32 mii_index) { + const auto& default_mii = RawData::DefaultMii[mii_index]; + core_data.SetDefault(); + + core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type)); + core_data.SetFacelineColor( + RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color))); + core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle)); + core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup)); + + core_data.SetHairType(static_cast<HairType>(default_mii.hair_type)); + core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color))); + core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip)); + core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type)); + core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color))); + core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale)); + core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect)); + core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate)); + core_data.SetEyeX(static_cast<u8>(default_mii.eye_x)); + core_data.SetEyeY(static_cast<u8>(default_mii.eye_y)); + + core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type)); + core_data.SetEyebrowColor( + RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color))); + core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale)); + core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect)); + core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate)); + core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x)); + core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y)); + + core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type)); + core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale)); + core_data.SetNoseY(static_cast<u8>(default_mii.nose_y)); + + core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type)); + core_data.SetMouthColor( + RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color))); + core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale)); + core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect)); + core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y)); + + core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type)); + core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type)); + core_data.SetBeardColor( + RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color))); + core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale)); + core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y)); + + core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type)); + core_data.SetGlassColor( + RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color))); + core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale)); + core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y)); + + core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type)); + core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale)); + core_data.SetMoleX(static_cast<u8>(default_mii.mole_x)); + core_data.SetMoleY(static_cast<u8>(default_mii.mole_y)); + + core_data.SetHeight(static_cast<u8>(default_mii.height)); + core_data.SetBuild(static_cast<u8>(default_mii.weight)); + core_data.SetGender(static_cast<Gender>(default_mii.gender)); + core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color)); + core_data.SetRegionMove(static_cast<u8>(default_mii.region_move)); + core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region)); + core_data.SetType(static_cast<u8>(default_mii.type)); + core_data.SetNickname(default_mii.nickname); + + const auto device_id = MiiUtil::GetDeviceId(); + create_id = MiiUtil::MakeCreateId(); + device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); + data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); +} + +void StoreData::BuildBase(Gender gender) { + const auto& default_mii = RawData::BaseMii[gender == Gender::Female ? 1 : 0]; + core_data.SetDefault(); + + core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type)); + core_data.SetFacelineColor( + RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color))); + core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle)); + core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup)); + + core_data.SetHairType(static_cast<HairType>(default_mii.hair_type)); + core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color))); + core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip)); + core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type)); + core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color))); + core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale)); + core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect)); + core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate)); + core_data.SetEyeX(static_cast<u8>(default_mii.eye_x)); + core_data.SetEyeY(static_cast<u8>(default_mii.eye_y)); + + core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type)); + core_data.SetEyebrowColor( + RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color))); + core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale)); + core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect)); + core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate)); + core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x)); + core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y)); + + core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type)); + core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale)); + core_data.SetNoseY(static_cast<u8>(default_mii.nose_y)); + + core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type)); + core_data.SetMouthColor( + RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color))); + core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale)); + core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect)); + core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y)); + + core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type)); + core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type)); + core_data.SetBeardColor( + RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color))); + core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale)); + core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y)); + + core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type)); + core_data.SetGlassColor( + RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color))); + core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale)); + core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y)); + + core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type)); + core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale)); + core_data.SetMoleX(static_cast<u8>(default_mii.mole_x)); + core_data.SetMoleY(static_cast<u8>(default_mii.mole_y)); + + core_data.SetHeight(static_cast<u8>(default_mii.height)); + core_data.SetBuild(static_cast<u8>(default_mii.weight)); + core_data.SetGender(static_cast<Gender>(default_mii.gender)); + core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color)); + core_data.SetRegionMove(static_cast<u8>(default_mii.region_move)); + core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region)); + core_data.SetType(static_cast<u8>(default_mii.type)); + core_data.SetNickname(default_mii.nickname); + + const auto device_id = MiiUtil::GetDeviceId(); + create_id = MiiUtil::MakeCreateId(); + device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); + data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); +} + +void StoreData::BuildRandom(Age age, Gender gender, Race race) { + core_data.BuildRandom(age, gender, race); + const auto device_id = MiiUtil::GetDeviceId(); + create_id = MiiUtil::MakeCreateId(); + device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); + data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); +} + +void StoreData::SetInvalidName() { + const auto& invalid_name = core_data.GetInvalidNickname(); + const auto device_id = MiiUtil::GetDeviceId(); + core_data.SetNickname(invalid_name); + device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); + data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); +} + +bool StoreData::IsSpecial() const { + return GetType() == 1; +} + +u32 StoreData::IsValid() const { + // TODO: complete this + return 0; +} + +void StoreData::SetFontRegion(FontRegion value) { + core_data.SetFontRegion(value); +} + +void StoreData::SetFavoriteColor(FavoriteColor value) { + core_data.SetFavoriteColor(value); +} + +void StoreData::SetGender(Gender value) { + core_data.SetGender(value); +} + +void StoreData::SetHeight(u8 value) { + core_data.SetHeight(value); +} + +void StoreData::SetBuild(u8 value) { + core_data.SetBuild(value); +} + +void StoreData::SetType(u8 value) { + core_data.SetType(value); +} + +void StoreData::SetRegionMove(u8 value) { + core_data.SetRegionMove(value); +} + +void StoreData::SetFacelineType(FacelineType value) { + core_data.SetFacelineType(value); +} + +void StoreData::SetFacelineColor(FacelineColor value) { + core_data.SetFacelineColor(value); +} + +void StoreData::SetFacelineWrinkle(FacelineWrinkle value) { + core_data.SetFacelineWrinkle(value); +} + +void StoreData::SetFacelineMake(FacelineMake value) { + core_data.SetFacelineMake(value); +} + +void StoreData::SetHairType(HairType value) { + core_data.SetHairType(value); +} + +void StoreData::SetHairColor(CommonColor value) { + core_data.SetHairColor(value); +} + +void StoreData::SetHairFlip(HairFlip value) { + core_data.SetHairFlip(value); +} + +void StoreData::SetEyeType(EyeType value) { + core_data.SetEyeType(value); +} + +void StoreData::SetEyeColor(CommonColor value) { + core_data.SetEyeColor(value); +} + +void StoreData::SetEyeScale(u8 value) { + core_data.SetEyeScale(value); +} + +void StoreData::SetEyeAspect(u8 value) { + core_data.SetEyeAspect(value); +} + +void StoreData::SetEyeRotate(u8 value) { + core_data.SetEyeRotate(value); +} + +void StoreData::SetEyeX(u8 value) { + core_data.SetEyeX(value); +} + +void StoreData::SetEyeY(u8 value) { + core_data.SetEyeY(value); +} + +void StoreData::SetEyebrowType(EyebrowType value) { + core_data.SetEyebrowType(value); +} + +void StoreData::SetEyebrowColor(CommonColor value) { + core_data.SetEyebrowColor(value); +} + +void StoreData::SetEyebrowScale(u8 value) { + core_data.SetEyebrowScale(value); +} + +void StoreData::SetEyebrowAspect(u8 value) { + core_data.SetEyebrowAspect(value); +} + +void StoreData::SetEyebrowRotate(u8 value) { + core_data.SetEyebrowRotate(value); +} + +void StoreData::SetEyebrowX(u8 value) { + core_data.SetEyebrowX(value); +} + +void StoreData::SetEyebrowY(u8 value) { + core_data.SetEyebrowY(value); +} + +void StoreData::SetNoseType(NoseType value) { + core_data.SetNoseType(value); +} + +void StoreData::SetNoseScale(u8 value) { + core_data.SetNoseScale(value); +} + +void StoreData::SetNoseY(u8 value) { + core_data.SetNoseY(value); +} + +void StoreData::SetMouthType(u8 value) { + core_data.SetMouthType(value); +} + +void StoreData::SetMouthColor(CommonColor value) { + core_data.SetMouthColor(value); +} + +void StoreData::SetMouthScale(u8 value) { + core_data.SetMouthScale(value); +} + +void StoreData::SetMouthAspect(u8 value) { + core_data.SetMouthAspect(value); +} + +void StoreData::SetMouthY(u8 value) { + core_data.SetMouthY(value); +} + +void StoreData::SetBeardColor(CommonColor value) { + core_data.SetBeardColor(value); +} + +void StoreData::SetBeardType(BeardType value) { + core_data.SetBeardType(value); +} + +void StoreData::SetMustacheType(MustacheType value) { + core_data.SetMustacheType(value); +} + +void StoreData::SetMustacheScale(u8 value) { + core_data.SetMustacheScale(value); +} + +void StoreData::SetMustacheY(u8 value) { + core_data.SetMustacheY(value); +} + +void StoreData::SetGlassType(GlassType value) { + core_data.SetGlassType(value); +} + +void StoreData::SetGlassColor(CommonColor value) { + core_data.SetGlassColor(value); +} + +void StoreData::SetGlassScale(u8 value) { + core_data.SetGlassScale(value); +} + +void StoreData::SetGlassY(u8 value) { + core_data.SetGlassY(value); +} + +void StoreData::SetMoleType(MoleType value) { + core_data.SetMoleType(value); +} + +void StoreData::SetMoleScale(u8 value) { + core_data.SetMoleScale(value); +} + +void StoreData::SetMoleX(u8 value) { + core_data.SetMoleX(value); +} + +void StoreData::SetMoleY(u8 value) { + core_data.SetMoleY(value); +} + +void StoreData::SetNickname(Nickname value) { + core_data.SetNickname(value); +} + +Common::UUID StoreData::GetCreateId() const { + return create_id; +} + +FontRegion StoreData::GetFontRegion() const { + return static_cast<FontRegion>(core_data.GetFontRegion()); +} + +FavoriteColor StoreData::GetFavoriteColor() const { + return core_data.GetFavoriteColor(); +} + +Gender StoreData::GetGender() const { + return core_data.GetGender(); +} + +u8 StoreData::GetHeight() const { + return core_data.GetHeight(); +} + +u8 StoreData::GetBuild() const { + return core_data.GetBuild(); +} + +u8 StoreData::GetType() const { + return core_data.GetType(); +} + +u8 StoreData::GetRegionMove() const { + return core_data.GetRegionMove(); +} + +FacelineType StoreData::GetFacelineType() const { + return core_data.GetFacelineType(); +} + +FacelineColor StoreData::GetFacelineColor() const { + return core_data.GetFacelineColor(); +} + +FacelineWrinkle StoreData::GetFacelineWrinkle() const { + return core_data.GetFacelineWrinkle(); +} + +FacelineMake StoreData::GetFacelineMake() const { + return core_data.GetFacelineMake(); +} + +HairType StoreData::GetHairType() const { + return core_data.GetHairType(); +} + +CommonColor StoreData::GetHairColor() const { + return core_data.GetHairColor(); +} + +HairFlip StoreData::GetHairFlip() const { + return core_data.GetHairFlip(); +} + +EyeType StoreData::GetEyeType() const { + return core_data.GetEyeType(); +} + +CommonColor StoreData::GetEyeColor() const { + return core_data.GetEyeColor(); +} + +u8 StoreData::GetEyeScale() const { + return core_data.GetEyeScale(); +} + +u8 StoreData::GetEyeAspect() const { + return core_data.GetEyeAspect(); +} + +u8 StoreData::GetEyeRotate() const { + return core_data.GetEyeRotate(); +} + +u8 StoreData::GetEyeX() const { + return core_data.GetEyeX(); +} + +u8 StoreData::GetEyeY() const { + return core_data.GetEyeY(); +} + +EyebrowType StoreData::GetEyebrowType() const { + return core_data.GetEyebrowType(); +} + +CommonColor StoreData::GetEyebrowColor() const { + return core_data.GetEyebrowColor(); +} + +u8 StoreData::GetEyebrowScale() const { + return core_data.GetEyebrowScale(); +} + +u8 StoreData::GetEyebrowAspect() const { + return core_data.GetEyebrowAspect(); +} + +u8 StoreData::GetEyebrowRotate() const { + return core_data.GetEyebrowRotate(); +} + +u8 StoreData::GetEyebrowX() const { + return core_data.GetEyebrowX(); +} + +u8 StoreData::GetEyebrowY() const { + return core_data.GetEyebrowY(); +} + +NoseType StoreData::GetNoseType() const { + return core_data.GetNoseType(); +} + +u8 StoreData::GetNoseScale() const { + return core_data.GetNoseScale(); +} + +u8 StoreData::GetNoseY() const { + return core_data.GetNoseY(); +} + +MouthType StoreData::GetMouthType() const { + return core_data.GetMouthType(); +} + +CommonColor StoreData::GetMouthColor() const { + return core_data.GetMouthColor(); +} + +u8 StoreData::GetMouthScale() const { + return core_data.GetMouthScale(); +} + +u8 StoreData::GetMouthAspect() const { + return core_data.GetMouthAspect(); +} + +u8 StoreData::GetMouthY() const { + return core_data.GetMouthY(); +} + +CommonColor StoreData::GetBeardColor() const { + return core_data.GetBeardColor(); +} + +BeardType StoreData::GetBeardType() const { + return core_data.GetBeardType(); +} + +MustacheType StoreData::GetMustacheType() const { + return core_data.GetMustacheType(); +} + +u8 StoreData::GetMustacheScale() const { + return core_data.GetMustacheScale(); +} + +u8 StoreData::GetMustacheY() const { + return core_data.GetMustacheY(); +} + +GlassType StoreData::GetGlassType() const { + return core_data.GetGlassType(); +} + +CommonColor StoreData::GetGlassColor() const { + return core_data.GetGlassColor(); +} + +u8 StoreData::GetGlassScale() const { + return core_data.GetGlassScale(); +} + +u8 StoreData::GetGlassY() const { + return core_data.GetGlassY(); +} + +MoleType StoreData::GetMoleType() const { + return core_data.GetMoleType(); +} + +u8 StoreData::GetMoleScale() const { + return core_data.GetMoleScale(); +} + +u8 StoreData::GetMoleX() const { + return core_data.GetMoleX(); +} + +u8 StoreData::GetMoleY() const { + return core_data.GetMoleY(); +} + +Nickname StoreData::GetNickname() const { + return core_data.GetNickname(); +} + +bool StoreData::operator==(const StoreData& data) { + bool is_identical = data.core_data.IsValid() == 0; + is_identical &= core_data.GetNickname().data == data.core_data.GetNickname().data; + is_identical &= GetCreateId() == data.GetCreateId(); + is_identical &= GetFontRegion() == data.GetFontRegion(); + is_identical &= GetFavoriteColor() == data.GetFavoriteColor(); + is_identical &= GetGender() == data.GetGender(); + is_identical &= GetHeight() == data.GetHeight(); + is_identical &= GetBuild() == data.GetBuild(); + is_identical &= GetType() == data.GetType(); + is_identical &= GetRegionMove() == data.GetRegionMove(); + is_identical &= GetFacelineType() == data.GetFacelineType(); + is_identical &= GetFacelineColor() == data.GetFacelineColor(); + is_identical &= GetFacelineWrinkle() == data.GetFacelineWrinkle(); + is_identical &= GetFacelineMake() == data.GetFacelineMake(); + is_identical &= GetHairType() == data.GetHairType(); + is_identical &= GetHairColor() == data.GetHairColor(); + is_identical &= GetHairFlip() == data.GetHairFlip(); + is_identical &= GetEyeType() == data.GetEyeType(); + is_identical &= GetEyeColor() == data.GetEyeColor(); + is_identical &= GetEyeScale() == data.GetEyeScale(); + is_identical &= GetEyeAspect() == data.GetEyeAspect(); + is_identical &= GetEyeRotate() == data.GetEyeRotate(); + is_identical &= GetEyeX() == data.GetEyeX(); + is_identical &= GetEyeY() == data.GetEyeY(); + is_identical &= GetEyebrowType() == data.GetEyebrowType(); + is_identical &= GetEyebrowColor() == data.GetEyebrowColor(); + is_identical &= GetEyebrowScale() == data.GetEyebrowScale(); + is_identical &= GetEyebrowAspect() == data.GetEyebrowAspect(); + is_identical &= GetEyebrowRotate() == data.GetEyebrowRotate(); + is_identical &= GetEyebrowX() == data.GetEyebrowX(); + is_identical &= GetEyebrowY() == data.GetEyebrowY(); + is_identical &= GetNoseType() == data.GetNoseType(); + is_identical &= GetNoseScale() == data.GetNoseScale(); + is_identical &= GetNoseY() == data.GetNoseY(); + is_identical &= GetMouthType() == data.GetMouthType(); + is_identical &= GetMouthColor() == data.GetMouthColor(); + is_identical &= GetMouthScale() == data.GetMouthScale(); + is_identical &= GetMouthAspect() == data.GetMouthAspect(); + is_identical &= GetMouthY() == data.GetMouthY(); + is_identical &= GetBeardColor() == data.GetBeardColor(); + is_identical &= GetBeardType() == data.GetBeardType(); + is_identical &= GetMustacheType() == data.GetMustacheType(); + is_identical &= GetMustacheScale() == data.GetMustacheScale(); + is_identical &= GetMustacheY() == data.GetMustacheY(); + is_identical &= GetGlassType() == data.GetGlassType(); + is_identical &= GetGlassColor() == data.GetGlassColor(); + is_identical &= GetGlassScale() == data.GetGlassScale(); + is_identical &= GetGlassY() == data.GetGlassY(); + is_identical &= GetMoleType() == data.GetMoleType(); + is_identical &= GetMoleScale() == data.GetMoleScale(); + is_identical &= GetMoleX() == data.GetMoleX(); + is_identical &= data.GetMoleY() == data.GetMoleY(); + return is_identical; +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h new file mode 100644 index 000000000..224c32cf8 --- /dev/null +++ b/src/core/hle/service/mii/types/store_data.h @@ -0,0 +1,145 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/mii/mii_types.h" +#include "core/hle/service/mii/types/core_data.h" + +namespace Service::Mii { + +class StoreData { +public: + // nn::mii::detail::StoreDataRaw::BuildDefault + void BuildDefault(u32 mii_index); + // nn::mii::detail::StoreDataRaw::BuildDefault + + void BuildBase(Gender gender); + // nn::mii::detail::StoreDataRaw::BuildRandom + void BuildRandom(Age age, Gender gender, Race race); + + bool IsSpecial() const; + + u32 IsValid() const; + + void SetFontRegion(FontRegion value); + void SetFavoriteColor(FavoriteColor value); + void SetGender(Gender value); + void SetHeight(u8 value); + void SetBuild(u8 value); + void SetType(u8 value); + void SetRegionMove(u8 value); + void SetFacelineType(FacelineType value); + void SetFacelineColor(FacelineColor value); + void SetFacelineWrinkle(FacelineWrinkle value); + void SetFacelineMake(FacelineMake value); + void SetHairType(HairType value); + void SetHairColor(CommonColor value); + void SetHairFlip(HairFlip value); + void SetEyeType(EyeType value); + void SetEyeColor(CommonColor value); + void SetEyeScale(u8 value); + void SetEyeAspect(u8 value); + void SetEyeRotate(u8 value); + void SetEyeX(u8 value); + void SetEyeY(u8 value); + void SetEyebrowType(EyebrowType value); + void SetEyebrowColor(CommonColor value); + void SetEyebrowScale(u8 value); + void SetEyebrowAspect(u8 value); + void SetEyebrowRotate(u8 value); + void SetEyebrowX(u8 value); + void SetEyebrowY(u8 value); + void SetNoseType(NoseType value); + void SetNoseScale(u8 value); + void SetNoseY(u8 value); + void SetMouthType(u8 value); + void SetMouthColor(CommonColor value); + void SetMouthScale(u8 value); + void SetMouthAspect(u8 value); + void SetMouthY(u8 value); + void SetBeardColor(CommonColor value); + void SetBeardType(BeardType value); + void SetMustacheType(MustacheType value); + void SetMustacheScale(u8 value); + void SetMustacheY(u8 value); + void SetGlassType(GlassType value); + void SetGlassColor(CommonColor value); + void SetGlassScale(u8 value); + void SetGlassY(u8 value); + void SetMoleType(MoleType value); + void SetMoleScale(u8 value); + void SetMoleX(u8 value); + void SetMoleY(u8 value); + void SetNickname(Nickname nickname); + void SetInvalidName(); + + Common::UUID GetCreateId() const; + FontRegion GetFontRegion() const; + FavoriteColor GetFavoriteColor() const; + Gender GetGender() const; + u8 GetHeight() const; + u8 GetBuild() const; + u8 GetType() const; + u8 GetRegionMove() const; + FacelineType GetFacelineType() const; + FacelineColor GetFacelineColor() const; + FacelineWrinkle GetFacelineWrinkle() const; + FacelineMake GetFacelineMake() const; + HairType GetHairType() const; + CommonColor GetHairColor() const; + HairFlip GetHairFlip() const; + EyeType GetEyeType() const; + CommonColor GetEyeColor() const; + u8 GetEyeScale() const; + u8 GetEyeAspect() const; + u8 GetEyeRotate() const; + u8 GetEyeX() const; + u8 GetEyeY() const; + EyebrowType GetEyebrowType() const; + CommonColor GetEyebrowColor() const; + u8 GetEyebrowScale() const; + u8 GetEyebrowAspect() const; + u8 GetEyebrowRotate() const; + u8 GetEyebrowX() const; + u8 GetEyebrowY() const; + NoseType GetNoseType() const; + u8 GetNoseScale() const; + u8 GetNoseY() const; + MouthType GetMouthType() const; + CommonColor GetMouthColor() const; + u8 GetMouthScale() const; + u8 GetMouthAspect() const; + u8 GetMouthY() const; + CommonColor GetBeardColor() const; + BeardType GetBeardType() const; + MustacheType GetMustacheType() const; + u8 GetMustacheScale() const; + u8 GetMustacheY() const; + GlassType GetGlassType() const; + CommonColor GetGlassColor() const; + u8 GetGlassScale() const; + u8 GetGlassY() const; + MoleType GetMoleType() const; + u8 GetMoleScale() const; + u8 GetMoleX() const; + u8 GetMoleY() const; + Nickname GetNickname() const; + + bool operator==(const StoreData& data); + +private: + CoreData core_data{}; + Common::UUID create_id{}; + u16 data_crc{}; + u16 device_crc{}; +}; +static_assert(sizeof(StoreData) == 0x44, "StoreData has incorrect size."); + +struct StoreDataElement { + StoreData store_data{}; + Source source{}; +}; +static_assert(sizeof(StoreDataElement) == 0x48, "StoreDataElement has incorrect size."); + +}; // namespace Service::Mii diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp new file mode 100644 index 000000000..1c28e0b1b --- /dev/null +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/mii/mii_util.h" +#include "core/hle/service/mii/types/raw_data.h" +#include "core/hle/service/mii/types/store_data.h" +#include "core/hle/service/mii/types/ver3_store_data.h" + +namespace Service::Mii { + +void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) { + faceline_color = static_cast<u8>(store_data.GetFacelineColor()) & 0xf; + hair_color = static_cast<u8>(store_data.GetHairColor()) & 0x7f; + eye_color = static_cast<u8>(store_data.GetEyeColor()) & 0x7f; + eyebrow_color = static_cast<u8>(store_data.GetEyebrowColor()) & 0x7f; + mouth_color = static_cast<u8>(store_data.GetMouthColor()) & 0x7f; + beard_color = static_cast<u8>(store_data.GetBeardColor()) & 0x7f; + glass_color = static_cast<u8>(store_data.GetGlassColor()) & 0x7f; + glass_type = static_cast<u8>(store_data.GetGlassType()) & 0x1f; +} + +void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { + out_store_data.BuildBase(Gender::Male); + + if (!IsValid()) { + return; + } + + // TODO: We are ignoring a bunch of data from the mii_v3 + + out_store_data.SetGender(static_cast<Gender>(mii_information.gender.Value())); + out_store_data.SetFavoriteColor( + static_cast<FavoriteColor>(mii_information.favorite_color.Value())); + out_store_data.SetHeight(height); + out_store_data.SetBuild(build); + + out_store_data.SetNickname(mii_name); + out_store_data.SetFontRegion( + static_cast<FontRegion>(static_cast<u8>(region_information.font_region))); + + out_store_data.SetFacelineType( + static_cast<FacelineType>(appearance_bits1.faceline_type.Value())); + out_store_data.SetFacelineColor( + static_cast<FacelineColor>(appearance_bits1.faceline_color.Value())); + out_store_data.SetFacelineWrinkle( + static_cast<FacelineWrinkle>(appearance_bits2.faceline_wrinkle.Value())); + out_store_data.SetFacelineMake( + static_cast<FacelineMake>(appearance_bits2.faceline_make.Value())); + + out_store_data.SetHairType(static_cast<HairType>(hair_type)); + out_store_data.SetHairColor(static_cast<CommonColor>(appearance_bits3.hair_color.Value())); + out_store_data.SetHairFlip(static_cast<HairFlip>(appearance_bits3.hair_flip.Value())); + + out_store_data.SetEyeType(static_cast<EyeType>(appearance_bits4.eye_type.Value())); + out_store_data.SetEyeColor(static_cast<CommonColor>(appearance_bits4.eye_color.Value())); + out_store_data.SetEyeScale(static_cast<u8>(appearance_bits4.eye_scale)); + out_store_data.SetEyeAspect(static_cast<u8>(appearance_bits4.eye_aspect)); + out_store_data.SetEyeRotate(static_cast<u8>(appearance_bits4.eye_rotate)); + out_store_data.SetEyeX(static_cast<u8>(appearance_bits4.eye_x)); + out_store_data.SetEyeY(static_cast<u8>(appearance_bits4.eye_y)); + + out_store_data.SetEyebrowType(static_cast<EyebrowType>(appearance_bits5.eyebrow_type.Value())); + out_store_data.SetEyebrowColor( + static_cast<CommonColor>(appearance_bits5.eyebrow_color.Value())); + out_store_data.SetEyebrowScale(static_cast<u8>(appearance_bits5.eyebrow_scale)); + out_store_data.SetEyebrowAspect(static_cast<u8>(appearance_bits5.eyebrow_aspect)); + out_store_data.SetEyebrowRotate(static_cast<u8>(appearance_bits5.eyebrow_rotate)); + out_store_data.SetEyebrowX(static_cast<u8>(appearance_bits5.eyebrow_x)); + out_store_data.SetEyebrowY(static_cast<u8>(appearance_bits5.eyebrow_y)); + + out_store_data.SetNoseType(static_cast<NoseType>(appearance_bits6.nose_type.Value())); + out_store_data.SetNoseScale(static_cast<u8>(appearance_bits6.nose_scale)); + out_store_data.SetNoseY(static_cast<u8>(appearance_bits6.nose_y)); + + out_store_data.SetMouthType(static_cast<u8>(appearance_bits7.mouth_type)); + out_store_data.SetMouthColor(static_cast<CommonColor>(appearance_bits7.mouth_color.Value())); + out_store_data.SetMouthScale(static_cast<u8>(appearance_bits7.mouth_scale)); + out_store_data.SetMouthAspect(static_cast<u8>(appearance_bits7.mouth_aspect)); + out_store_data.SetMouthY(static_cast<u8>(appearance_bits8.mouth_y)); + + out_store_data.SetMustacheType( + static_cast<MustacheType>(appearance_bits8.mustache_type.Value())); + out_store_data.SetMustacheScale(static_cast<u8>(appearance_bits9.mustache_scale)); + out_store_data.SetMustacheY(static_cast<u8>(appearance_bits9.mustache_y)); + + out_store_data.SetBeardType(static_cast<BeardType>(appearance_bits9.beard_type.Value())); + out_store_data.SetBeardColor(static_cast<CommonColor>(appearance_bits9.beard_color.Value())); + + out_store_data.SetGlassType(static_cast<GlassType>(appearance_bits10.glass_type.Value())); + out_store_data.SetGlassColor(static_cast<CommonColor>(appearance_bits10.glass_color.Value())); + out_store_data.SetGlassScale(static_cast<u8>(appearance_bits10.glass_scale)); + out_store_data.SetGlassY(static_cast<u8>(appearance_bits10.glass_y)); + + out_store_data.SetMoleType(static_cast<MoleType>(appearance_bits11.mole_type.Value())); + out_store_data.SetMoleScale(static_cast<u8>(appearance_bits11.mole_scale)); + out_store_data.SetMoleX(static_cast<u8>(appearance_bits11.mole_x)); + out_store_data.SetMoleY(static_cast<u8>(appearance_bits11.mole_y)); +} + +void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { + version = 1; + mii_information.gender.Assign(static_cast<u8>(store_data.GetGender())); + mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor())); + height = store_data.GetHeight(); + build = store_data.GetBuild(); + + mii_name = store_data.GetNickname(); + region_information.font_region.Assign(static_cast<u8>(store_data.GetFontRegion())); + + appearance_bits1.faceline_type.Assign(static_cast<u8>(store_data.GetFacelineType())); + appearance_bits2.faceline_wrinkle.Assign(static_cast<u8>(store_data.GetFacelineWrinkle())); + appearance_bits2.faceline_make.Assign(static_cast<u8>(store_data.GetFacelineMake())); + + hair_type = static_cast<u8>(store_data.GetHairType()); + appearance_bits3.hair_flip.Assign(static_cast<u8>(store_data.GetHairFlip())); + + appearance_bits4.eye_type.Assign(static_cast<u8>(store_data.GetEyeType())); + appearance_bits4.eye_scale.Assign(store_data.GetEyeScale()); + appearance_bits4.eye_aspect.Assign(store_data.GetEyebrowAspect()); + appearance_bits4.eye_rotate.Assign(store_data.GetEyeRotate()); + appearance_bits4.eye_x.Assign(store_data.GetEyeX()); + appearance_bits4.eye_y.Assign(store_data.GetEyeY()); + + appearance_bits5.eyebrow_type.Assign(static_cast<u8>(store_data.GetEyebrowType())); + appearance_bits5.eyebrow_scale.Assign(store_data.GetEyebrowScale()); + appearance_bits5.eyebrow_aspect.Assign(store_data.GetEyebrowAspect()); + appearance_bits5.eyebrow_rotate.Assign(store_data.GetEyebrowRotate()); + appearance_bits5.eyebrow_x.Assign(store_data.GetEyebrowX()); + appearance_bits5.eyebrow_y.Assign(store_data.GetEyebrowY()); + + appearance_bits6.nose_type.Assign(static_cast<u8>(store_data.GetNoseType())); + appearance_bits6.nose_scale.Assign(store_data.GetNoseScale()); + appearance_bits6.nose_y.Assign(store_data.GetNoseY()); + + appearance_bits7.mouth_type.Assign(static_cast<u8>(store_data.GetMouthType())); + appearance_bits7.mouth_scale.Assign(store_data.GetMouthScale()); + appearance_bits7.mouth_aspect.Assign(store_data.GetMouthAspect()); + appearance_bits8.mouth_y.Assign(store_data.GetMouthY()); + + appearance_bits8.mustache_type.Assign(static_cast<u8>(store_data.GetMustacheType())); + appearance_bits9.mustache_scale.Assign(store_data.GetMustacheScale()); + appearance_bits9.mustache_y.Assign(store_data.GetMustacheY()); + + appearance_bits9.beard_type.Assign(static_cast<u8>(store_data.GetBeardType())); + + appearance_bits10.glass_scale.Assign(store_data.GetGlassScale()); + appearance_bits10.glass_y.Assign(store_data.GetGlassY()); + + appearance_bits11.mole_type.Assign(static_cast<u8>(store_data.GetMoleType())); + appearance_bits11.mole_scale.Assign(store_data.GetMoleScale()); + appearance_bits11.mole_x.Assign(store_data.GetMoleX()); + appearance_bits11.mole_y.Assign(store_data.GetMoleY()); + + // These types are converted to V3 from a table + appearance_bits1.faceline_color.Assign( + RawData::FromVer3GetFacelineColor(static_cast<u8>(store_data.GetFacelineColor()))); + appearance_bits3.hair_color.Assign( + RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetHairColor()))); + appearance_bits4.eye_color.Assign( + RawData::FromVer3GetEyeColor(static_cast<u8>(store_data.GetEyeColor()))); + appearance_bits5.eyebrow_color.Assign( + RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetEyebrowColor()))); + appearance_bits7.mouth_color.Assign( + RawData::FromVer3GetMouthlineColor(static_cast<u8>(store_data.GetMouthColor()))); + appearance_bits9.beard_color.Assign( + RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetBeardColor()))); + appearance_bits10.glass_color.Assign( + RawData::FromVer3GetGlassColor(static_cast<u8>(store_data.GetGlassColor()))); + appearance_bits10.glass_type.Assign( + RawData::FromVer3GetGlassType(static_cast<u8>(store_data.GetGlassType()))); + + crc = MiiUtil::CalculateCrc16(&version, sizeof(Ver3StoreData) - sizeof(u16)); +} + +u32 Ver3StoreData::IsValid() const { + bool is_valid = version == 0 || version == 3; + + is_valid = is_valid && (mii_name.data[0] != '\0'); + + is_valid = is_valid && (mii_information.birth_month < 13); + is_valid = is_valid && (mii_information.birth_day < 32); + is_valid = is_valid && (mii_information.favorite_color <= static_cast<u8>(FavoriteColor::Max)); + is_valid = is_valid && (height <= MaxHeight); + is_valid = is_valid && (build <= MaxBuild); + + is_valid = is_valid && (appearance_bits1.faceline_type <= static_cast<u8>(FacelineType::Max)); + is_valid = is_valid && (appearance_bits1.faceline_color <= MaxVer3CommonColor - 2); + is_valid = + is_valid && (appearance_bits2.faceline_wrinkle <= static_cast<u8>(FacelineWrinkle::Max)); + is_valid = is_valid && (appearance_bits2.faceline_make <= static_cast<u8>(FacelineMake::Max)); + + is_valid = is_valid && (hair_type <= static_cast<u8>(HairType::Max)); + is_valid = is_valid && (appearance_bits3.hair_color <= MaxVer3CommonColor); + + is_valid = is_valid && (appearance_bits4.eye_type <= static_cast<u8>(EyeType::Max)); + is_valid = is_valid && (appearance_bits4.eye_color <= MaxVer3CommonColor - 2); + is_valid = is_valid && (appearance_bits4.eye_scale <= MaxEyeScale); + is_valid = is_valid && (appearance_bits4.eye_aspect <= MaxEyeAspect); + is_valid = is_valid && (appearance_bits4.eye_rotate <= MaxEyeRotate); + is_valid = is_valid && (appearance_bits4.eye_x <= MaxEyeX); + is_valid = is_valid && (appearance_bits4.eye_y <= MaxEyeY); + + is_valid = is_valid && (appearance_bits5.eyebrow_type <= static_cast<u8>(EyebrowType::Max)); + is_valid = is_valid && (appearance_bits5.eyebrow_color <= MaxVer3CommonColor); + is_valid = is_valid && (appearance_bits5.eyebrow_scale <= MaxEyebrowScale); + is_valid = is_valid && (appearance_bits5.eyebrow_aspect <= MaxEyebrowAspect); + is_valid = is_valid && (appearance_bits5.eyebrow_rotate <= MaxEyebrowRotate); + is_valid = is_valid && (appearance_bits5.eyebrow_x <= MaxEyebrowX); + is_valid = is_valid && (appearance_bits5.eyebrow_y <= MaxEyebrowY); + + is_valid = is_valid && (appearance_bits6.nose_type <= static_cast<u8>(NoseType::Max)); + is_valid = is_valid && (appearance_bits6.nose_scale <= MaxNoseScale); + is_valid = is_valid && (appearance_bits6.nose_y <= MaxNoseY); + + is_valid = is_valid && (appearance_bits7.mouth_type <= static_cast<u8>(MouthType::Max)); + is_valid = is_valid && (appearance_bits7.mouth_color <= MaxVer3CommonColor - 3); + is_valid = is_valid && (appearance_bits7.mouth_scale <= MaxMouthScale); + is_valid = is_valid && (appearance_bits7.mouth_aspect <= MaxMoutAspect); + is_valid = is_valid && (appearance_bits8.mouth_y <= MaxMouthY); + + is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast<u8>(MustacheType::Max)); + is_valid = is_valid && (appearance_bits9.mustache_scale < MaxMustacheScale); + is_valid = is_valid && (appearance_bits9.mustache_y <= MasMustacheY); + + is_valid = is_valid && (appearance_bits9.beard_type <= static_cast<u8>(BeardType::Max)); + is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor); + + is_valid = is_valid && (appearance_bits10.glass_type <= MaxVer3GlassType); + is_valid = is_valid && (appearance_bits10.glass_color <= MaxVer3CommonColor - 2); + is_valid = is_valid && (appearance_bits10.glass_scale <= MaxGlassScale); + is_valid = is_valid && (appearance_bits10.glass_y <= MaxGlassScale); + + is_valid = is_valid && (appearance_bits11.mole_type <= static_cast<u8>(MoleType::Max)); + is_valid = is_valid && (appearance_bits11.mole_scale <= MaxMoleScale); + is_valid = is_valid && (appearance_bits11.mole_x <= MaxMoleX); + is_valid = is_valid && (appearance_bits11.mole_y <= MaxMoleY); + + return is_valid; +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/types/ver3_store_data.h b/src/core/hle/service/mii/types/ver3_store_data.h new file mode 100644 index 000000000..47907bf7d --- /dev/null +++ b/src/core/hle/service/mii/types/ver3_store_data.h @@ -0,0 +1,160 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/mii/mii_types.h" + +namespace Service::Mii { +class StoreData; + +// This is nn::mii::Ver3StoreData +// Based on citra HLE::Applets::MiiData and PretendoNetwork. +// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48 +// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299 + +struct NfpStoreDataExtension { + void SetFromStoreData(const StoreData& store_data); + + u8 faceline_color; + u8 hair_color; + u8 eye_color; + u8 eyebrow_color; + u8 mouth_color; + u8 beard_color; + u8 glass_color; + u8 glass_type; +}; +static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size"); + +#pragma pack(push, 4) +class Ver3StoreData { +public: + void BuildToStoreData(StoreData& out_store_data) const; + void BuildFromStoreData(const StoreData& store_data); + + u32 IsValid() const; + + u8 version; + union { + u8 raw; + + BitField<0, 1, u8> allow_copying; + BitField<1, 1, u8> profanity_flag; + BitField<2, 2, u8> region_lock; + BitField<4, 2, u8> font_region; + } region_information; + u16_be mii_id; + u64_be system_id; + u32_be specialness_and_creation_date; + std::array<u8, 6> creator_mac; + u16_be padding; + union { + u16 raw; + + BitField<0, 1, u16> gender; + BitField<1, 4, u16> birth_month; + BitField<5, 5, u16> birth_day; + BitField<10, 4, u16> favorite_color; + BitField<14, 1, u16> favorite; + } mii_information; + Nickname mii_name; + u8 height; + u8 build; + union { + u8 raw; + + BitField<0, 1, u8> disable_sharing; + BitField<1, 4, u8> faceline_type; + BitField<5, 3, u8> faceline_color; + } appearance_bits1; + union { + u8 raw; + + BitField<0, 4, u8> faceline_wrinkle; + BitField<4, 4, u8> faceline_make; + } appearance_bits2; + u8 hair_type; + union { + u8 raw; + + BitField<0, 3, u8> hair_color; + BitField<3, 1, u8> hair_flip; + } appearance_bits3; + union { + u32 raw; + + BitField<0, 6, u32> eye_type; + BitField<6, 3, u32> eye_color; + BitField<9, 4, u32> eye_scale; + BitField<13, 3, u32> eye_aspect; + BitField<16, 5, u32> eye_rotate; + BitField<21, 4, u32> eye_x; + BitField<25, 5, u32> eye_y; + } appearance_bits4; + union { + u32 raw; + + BitField<0, 5, u32> eyebrow_type; + BitField<5, 3, u32> eyebrow_color; + BitField<8, 4, u32> eyebrow_scale; + BitField<12, 3, u32> eyebrow_aspect; + BitField<16, 4, u32> eyebrow_rotate; + BitField<21, 4, u32> eyebrow_x; + BitField<25, 5, u32> eyebrow_y; + } appearance_bits5; + union { + u16 raw; + + BitField<0, 5, u16> nose_type; + BitField<5, 4, u16> nose_scale; + BitField<9, 5, u16> nose_y; + } appearance_bits6; + union { + u16 raw; + + BitField<0, 6, u16> mouth_type; + BitField<6, 3, u16> mouth_color; + BitField<9, 4, u16> mouth_scale; + BitField<13, 3, u16> mouth_aspect; + } appearance_bits7; + union { + u8 raw; + + BitField<0, 5, u8> mouth_y; + BitField<5, 3, u8> mustache_type; + } appearance_bits8; + u8 allow_copying; + union { + u16 raw; + + BitField<0, 3, u16> beard_type; + BitField<3, 3, u16> beard_color; + BitField<6, 4, u16> mustache_scale; + BitField<10, 5, u16> mustache_y; + } appearance_bits9; + union { + u16 raw; + + BitField<0, 4, u16> glass_type; + BitField<4, 3, u16> glass_color; + BitField<7, 4, u16> glass_scale; + BitField<11, 5, u16> glass_y; + } appearance_bits10; + union { + u16 raw; + + BitField<0, 1, u16> mole_type; + BitField<1, 4, u16> mole_scale; + BitField<5, 5, u16> mole_x; + BitField<10, 5, u16> mole_y; + } appearance_bits11; + + Nickname author_name; + INSERT_PADDING_BYTES(0x2); + u16_be crc; +}; +static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size"); +#pragma pack(pop) + +}; // namespace Service::Mii diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 49446bc42..5dda12343 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -28,7 +28,6 @@ #include "core/hle/kernel/k_event.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/mii/mii_manager.h" -#include "core/hle/service/mii/types.h" #include "core/hle/service/nfc/common/amiibo_crypto.h" #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/mifare_result.h" @@ -681,12 +680,16 @@ Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const { return ResultRegistrationIsNotInitialized; } - Service::Mii::MiiManager manager; + Mii::CharInfo char_info{}; + Mii::StoreData store_data{}; + tag_data.owner_mii.BuildToStoreData(store_data); + char_info.SetFromStoreData(store_data); + const auto& settings = tag_data.settings; // TODO: Validate this data register_info = { - .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), + .mii_char_info = char_info, .creation_date = settings.init_date.GetWriteDate(), .amiibo_name = GetAmiiboName(settings), .font_region = settings.settings.font_region, @@ -825,8 +828,11 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe return ResultWrongDeviceState; } - Service::Mii::MiiManager manager; - const auto mii = manager.BuildDefault(0); + Service::Mii::StoreData store_data{}; + Service::Mii::NfpStoreDataExtension extension{}; + store_data.BuildBase(Mii::Gender::Male); + extension.SetFromStoreData(store_data); + auto& settings = tag_data.settings; if (tag_data.settings.settings.amiibo_initialized == 0) { @@ -835,8 +841,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe } SetAmiiboName(settings, register_info.amiibo_name); - tag_data.owner_mii = manager.BuildFromStoreData(mii); - tag_data.mii_extension = manager.SetFromStoreData(mii); + tag_data.owner_mii.BuildFromStoreData(store_data); + tag_data.mii_extension = extension; tag_data.unknown = 0; tag_data.unknown2 = {}; settings.country_code_id = 0; @@ -1453,7 +1459,7 @@ void NfcDevice::UpdateRegisterInfoCrc() { void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, const NFP::EncryptedNTAG215File& encrypted_file) const { - Service::Mii::MiiManager manager; + Service::Mii::StoreData store_data{}; auto& settings = stubbed_tag_data.settings; stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file); @@ -1467,7 +1473,8 @@ void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'}); settings.settings.font_region.Assign(0); settings.init_date = GetAmiiboDate(GetCurrentPosixTime()); - stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); + store_data.BuildBase(Mii::Gender::Male); + stubbed_tag_data.owner_mii.BuildFromStoreData(store_data); // Admin info settings.settings.amiibo_initialized.Assign(1); diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index aed12a7f8..f96d21220 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -6,7 +6,9 @@ #include <array> #include "common/swap.h" -#include "core/hle/service/mii/types.h" +#include "core/hle/service/mii/types/char_info.h" +#include "core/hle/service/mii/types/store_data.h" +#include "core/hle/service/mii/types/ver3_store_data.h" #include "core/hle/service/nfc/nfc_types.h" namespace Service::NFP { @@ -322,7 +324,7 @@ static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); // This is nn::nfp::RegisterInfoPrivate struct RegisterInfoPrivate { - Service::Mii::MiiStoreData mii_store_data; + Service::Mii::StoreData mii_store_data; WriteDate creation_date; AmiiboName amiibo_name; u8 font_region; diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index a51ca5444..0ca05257e 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -160,8 +160,8 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) { u32 address{}; auto& smmu_allocator = host1x.Allocator(); auto& smmu_memory_manager = host1x.MemoryManager(); - while (!(address = - smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) { + while ((address = smmu_allocator.Allocate( + static_cast<u32>(handle_description->aligned_size))) == 0) { // Free handles until the allocation succeeds std::scoped_lock queueLock(unmap_queue_lock); if (auto freeHandleDesc{unmap_queue.front()}) { diff --git a/src/core/hle/service/ssl/ssl_backend_schannel.cpp b/src/core/hle/service/ssl/ssl_backend_schannel.cpp index d834a0c1f..212057cfc 100644 --- a/src/core/hle/service/ssl/ssl_backend_schannel.cpp +++ b/src/core/hle/service/ssl/ssl_backend_schannel.cpp @@ -477,7 +477,8 @@ public: return ResultInternalError; } PCCERT_CONTEXT some_cert = nullptr; - while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert))) { + while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert)) != + nullptr) { out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded), static_cast<u8*>(some_cert->pbCertEncoded) + some_cert->cbCertEncoded); diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 5d28300e6..a983f23ea 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -48,15 +48,32 @@ enum class CallType { using socklen_t = int; +SOCKET interrupt_socket = static_cast<SOCKET>(-1); + +void InterruptSocketOperations() { + closesocket(interrupt_socket); +} + +void AcknowledgeInterrupt() { + interrupt_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +} + void Initialize() { WSADATA wsa_data; (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); + + AcknowledgeInterrupt(); } void Finalize() { + InterruptSocketOperations(); WSACleanup(); } +SOCKET GetInterruptSocket() { + return interrupt_socket; +} + sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -157,9 +174,42 @@ constexpr int SD_RECEIVE = SHUT_RD; constexpr int SD_SEND = SHUT_WR; constexpr int SD_BOTH = SHUT_RDWR; -void Initialize() {} +int interrupt_pipe_fd[2] = {-1, -1}; -void Finalize() {} +void Initialize() { + if (pipe(interrupt_pipe_fd) != 0) { + LOG_ERROR(Network, "Failed to create interrupt pipe!"); + } + int flags = fcntl(interrupt_pipe_fd[0], F_GETFL); + ASSERT_MSG(fcntl(interrupt_pipe_fd[0], F_SETFL, flags | O_NONBLOCK) == 0, + "Failed to set nonblocking state for interrupt pipe"); +} + +void Finalize() { + if (interrupt_pipe_fd[0] >= 0) { + close(interrupt_pipe_fd[0]); + } + if (interrupt_pipe_fd[1] >= 0) { + close(interrupt_pipe_fd[1]); + } +} + +void InterruptSocketOperations() { + u8 value = 0; + ASSERT(write(interrupt_pipe_fd[1], &value, sizeof(value)) == 1); +} + +void AcknowledgeInterrupt() { + u8 value = 0; + ssize_t ret = read(interrupt_pipe_fd[0], &value, sizeof(value)); + if (ret != 1 && errno != EAGAIN && errno != EWOULDBLOCK) { + LOG_ERROR(Network, "Failed to acknowledge interrupt on shutdown"); + } +} + +SOCKET GetInterruptSocket() { + return interrupt_pipe_fd[0]; +} sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -490,6 +540,14 @@ NetworkInstance::~NetworkInstance() { Finalize(); } +void CancelPendingSocketOperations() { + InterruptSocketOperations(); +} + +void RestartSocketOperations() { + AcknowledgeInterrupt(); +} + std::optional<IPv4Address> GetHostIPv4Address() { const auto network_interface = Network::GetSelectedNetworkInterface(); if (!network_interface.has_value()) { @@ -560,7 +618,14 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { return result; }); - const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); + host_pollfds.push_back(WSAPOLLFD{ + .fd = GetInterruptSocket(), + .events = POLLIN, + .revents = 0, + }); + + const int result = + WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), timeout); if (result == 0) { ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), [](WSAPOLLFD fd) { return fd.revents == 0; })); @@ -627,6 +692,24 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { sockaddr_in addr; socklen_t addrlen = sizeof(addr); + + std::vector<WSAPOLLFD> host_pollfds{ + WSAPOLLFD{fd, POLLIN, 0}, + WSAPOLLFD{GetInterruptSocket(), POLLIN, 0}, + }; + + while (true) { + const int pollres = + WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1); + if (host_pollfds[1].revents != 0) { + // Interrupt signaled before a client could be accepted, break + return {AcceptResult{}, Errno::AGAIN}; + } + if (pollres > 0) { + break; + } + } + const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen); if (new_socket == INVALID_SOCKET) { diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index c7e20ae34..b7b7d773a 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h @@ -96,6 +96,9 @@ public: ~NetworkInstance(); }; +void CancelPendingSocketOperations(); +void RestartSocketOperations(); + #ifdef _WIN32 constexpr IPv4Address TranslateIPv4(in_addr addr) { auto& bytes = addr.S_un.S_un_b; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 07c65dc1a..b6e355622 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<const char*, 66> RESULT_MESSAGES{ +constexpr std::array<const char*, 68> 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<const char*, 66> 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 721eb8e8c..b4828f7cd 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -3,6 +3,7 @@ #pragma once +#include <functional> #include <iosfwd> #include <memory> #include <optional> @@ -132,6 +133,8 @@ enum class ResultStatus : u16 { ErrorBLZDecompressionFailed, ErrorBadINIHeader, ErrorINITooManyKIPs, + ErrorIntegrityVerificationNotImplemented, + ErrorIntegrityVerificationFailed, }; std::string GetResultStatusString(ResultStatus status); @@ -170,6 +173,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<bool(size_t, size_t)> progress_callback) { + return ResultStatus::ErrorIntegrityVerificationNotImplemented; + } + + /** * Get the code (typically .code section) of the application * * @param[out] buffer Reference to buffer to store data diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 09d40e695..4feb6968a 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -3,6 +3,8 @@ #include <utility> +#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<bool(size_t, size_t)> 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<u8> 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<u8, NcaSha256HashLength> 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 cf356ce63..96779e27f 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<bool(size_t, size_t)> 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 f9b2549a3..fe2af1ae6 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<bool(size_t, size_t)> 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 79df4586a..7ce436c67 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<bool(size_t, size_t)> 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 3a76bc788..12d72c380 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<bool(size_t, size_t)> 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 ff05e6f62..b02e136d3 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<bool(size_t, size_t)> 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/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp index d707dabe2..93038f161 100644 --- a/src/dedicated_room/yuzu_room.cpp +++ b/src/dedicated_room/yuzu_room.cpp @@ -368,9 +368,9 @@ int main(int argc, char** argv) { if (auto room = network.GetRoom().lock()) { AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game, .id = preferred_game_id}; - if (!room->Create(room_name, room_description, bind_address, port, password, max_members, - username, preferred_game_info, std::move(verify_backend), ban_list, - enable_yuzu_mods)) { + if (!room->Create(room_name, room_description, bind_address, static_cast<u16>(port), + password, max_members, username, preferred_game_info, + std::move(verify_backend), ban_list, enable_yuzu_mods)) { LOG_INFO(Network, "Failed to create room: "); return -1; } diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 322c29065..5c127c8ef 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -37,8 +37,6 @@ add_library(input_common STATIC if (MSVC) target_compile_options(input_common PRIVATE - /W4 - /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss diff --git a/src/network/room.cpp b/src/network/room.cpp index e456ea09c..d87db37de 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -805,7 +805,7 @@ IPv4Address Room::RoomImpl::GenerateFakeIPAddress() { std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE do { for (std::size_t i = 2; i < result_ip.size(); ++i) { - result_ip[i] = dis(random_gen); + result_ip[i] = static_cast<u8>(dis(random_gen)); } } while (!IsValidFakeIPAddress(result_ip)); diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 07e75f9d8..83b763447 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -245,8 +245,6 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit) if (MSVC) target_compile_options(shader_recompiler PRIVATE - /W4 - /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 34240b36f..8decdf399 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -204,9 +204,7 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind if (def.count > 1) { throw NotImplementedException("Indirect texture sample"); } - const Id sampler_id{def.id}; - const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)}; - return ctx.OpImage(ctx.image_buffer_type, id); + return ctx.OpLoad(ctx.image_buffer_type, def.id); } else { const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; if (def.count > 1) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index bec5db173..72f69b7aa 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]}; @@ -1242,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) { @@ -1271,7 +1275,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)}; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index e63330f11..7c49fd504 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -206,7 +206,6 @@ public: Id output_u32{}; Id image_buffer_type{}; - Id sampled_texture_buffer_type{}; Id image_u32{}; std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{}; diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp index 753c62098..e593132e6 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp @@ -161,7 +161,8 @@ enum class SpecialRegister : u64 { LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY"); return ir.Imm32(0); // This is the default value hardware returns. default: - throw NotImplementedException("S2R special register {}", special_register); + LOG_CRITICAL(Shader, "(STUBBED) Special register {}", special_register); + return ir.Imm32(0); // This is the default value hardware returns. } } } // Anonymous namespace diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index e85f9977b..b6e3bc875 100644 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp @@ -55,7 +55,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { // Pushing more values than space available should partially succeed. { std::vector<char> to_push(6); - std::iota(to_push.begin(), to_push.end(), 88); + std::iota(to_push.begin(), to_push.end(), static_cast<char>(88)); const std::size_t count = buf.Push(to_push); REQUIRE(count == 3U); } diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 9f1b340a9..58ce0d8c2 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -14,6 +14,7 @@ namespace Tegra { constexpr u32 MacroRegistersStart = 0xE00; +constexpr u32 ComputeInline = 0x6D; DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_) @@ -83,12 +84,35 @@ bool DmaPusher::Step() { dma_state.dma_get, command_list_header.size * sizeof(u32)); } } - Core::Memory::GpuGuestMemory<Tegra::CommandHeader, - Core::Memory::GuestMemoryFlags::UnsafeRead> - headers(memory_manager, dma_state.dma_get, command_list_header.size, &command_headers); - ProcessCommands(headers); + const auto safe_process = [&] { + Core::Memory::GpuGuestMemory<Tegra::CommandHeader, + Core::Memory::GuestMemoryFlags::SafeRead> + headers(memory_manager, dma_state.dma_get, command_list_header.size, + &command_headers); + ProcessCommands(headers); + }; + const auto unsafe_process = [&] { + Core::Memory::GpuGuestMemory<Tegra::CommandHeader, + Core::Memory::GuestMemoryFlags::UnsafeRead> + headers(memory_manager, dma_state.dma_get, command_list_header.size, + &command_headers); + ProcessCommands(headers); + }; + if (Settings::IsGPULevelHigh()) { + if (dma_state.method >= MacroRegistersStart) { + unsafe_process(); + return true; + } + if (subchannel_type[dma_state.subchannel] == Engines::EngineTypes::KeplerCompute && + dma_state.method == ComputeInline) { + unsafe_process(); + return true; + } + safe_process(); + return true; + } + unsafe_process(); } - return true; } diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 8a2784cdc..c9fab2d90 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -130,8 +130,10 @@ public: void DispatchCalls(); - void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id) { + void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, + Engines::EngineTypes engine_type) { subchannels[subchannel_id] = engine; + subchannel_type[subchannel_id] = engine_type; } void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); @@ -170,6 +172,7 @@ private: const bool ib_enable{true}; ///< IB mode enabled std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; + std::array<Engines::EngineTypes, max_subchannels> subchannel_type; GPU& gpu; Core::System& system; diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h index 392322358..54631ee6c 100644 --- a/src/video_core/engines/engine_interface.h +++ b/src/video_core/engines/engine_interface.h @@ -11,6 +11,14 @@ namespace Tegra::Engines { +enum class EngineTypes : u32 { + KeplerCompute, + Maxwell3D, + Fermi2D, + MaxwellDMA, + KeplerMemory, +}; + class EngineInterface { public: virtual ~EngineInterface() = default; diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h index 7242d2529..21bf8aeb4 100644 --- a/src/video_core/engines/engine_upload.h +++ b/src/video_core/engines/engine_upload.h @@ -69,6 +69,14 @@ public: /// Binds a rasterizer to this engine. void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + GPUVAddr ExecTargetAddress() const { + return regs.dest.Address(); + } + + u32 GetUploadSize() const { + return copy_size; + } + private: void ProcessData(std::span<const u8> read_buffer); diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index a38d9528a..cd61ab222 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -43,16 +43,33 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal switch (method) { case KEPLER_COMPUTE_REG_INDEX(exec_upload): { + UploadInfo info{.upload_address = upload_address, + .exec_address = upload_state.ExecTargetAddress(), + .copy_size = upload_state.GetUploadSize()}; + uploads.push_back(info); upload_state.ProcessExec(regs.exec_upload.linear != 0); break; } case KEPLER_COMPUTE_REG_INDEX(data_upload): { + upload_address = current_dma_segment; upload_state.ProcessData(method_argument, is_last_call); break; } - case KEPLER_COMPUTE_REG_INDEX(launch): + case KEPLER_COMPUTE_REG_INDEX(launch): { + const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); + + for (auto& data : uploads) { + const GPUVAddr offset = data.exec_address - launch_desc_loc; + if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) && + memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) { + indirect_compute = {data.upload_address}; + } + } + uploads.clear(); ProcessLaunch(); + indirect_compute = std::nullopt; break; + } default: break; } @@ -62,6 +79,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun u32 methods_pending) { switch (method) { case KEPLER_COMPUTE_REG_INDEX(data_upload): + upload_address = current_dma_segment; upload_state.ProcessData(base_start, amount); return; default: diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 2092e685f..735e05fb4 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -5,6 +5,7 @@ #include <array> #include <cstddef> +#include <optional> #include <vector> #include "common/bit_field.h" #include "common/common_funcs.h" @@ -36,6 +37,9 @@ namespace Tegra::Engines { #define KEPLER_COMPUTE_REG_INDEX(field_name) \ (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) +#define LAUNCH_REG_INDEX(field_name) \ + (offsetof(Tegra::Engines::KeplerCompute::LaunchParams, field_name) / sizeof(u32)) + class KeplerCompute final : public EngineInterface { public: explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); @@ -201,6 +205,10 @@ public: void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) override; + std::optional<GPUVAddr> GetIndirectComputeAddress() const { + return indirect_compute; + } + private: void ProcessLaunch(); @@ -216,6 +224,15 @@ private: MemoryManager& memory_manager; VideoCore::RasterizerInterface* rasterizer = nullptr; Upload::State upload_state; + GPUVAddr upload_address; + + struct UploadInfo { + GPUVAddr upload_address; + GPUVAddr exec_address; + u32 copy_size; + }; + std::vector<UploadInfo> uploads; + std::optional<GPUVAddr> indirect_compute{}; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index c3696096d..06e349e43 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -257,6 +257,7 @@ u32 Maxwell3D::GetMaxCurrentVertices() { const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); num_vertices = std::max( num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value())); + break; } return num_vertices; } @@ -269,10 +270,13 @@ size_t Maxwell3D::EstimateIndexBufferSize() { std::numeric_limits<u32>::max()}; const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); const size_t log2_byte_size = Common::Log2Ceil64(byte_size); + const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; + const size_t lower_cap = + std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); return std::min<size_t>( memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) / byte_size, - static_cast<size_t>(end_address - start_address)); + lower_cap); } u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 7718a09b3..6de2543b7 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -34,19 +34,24 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) { bound_engines[method_call.subchannel] = engine_id; switch (engine_id) { case EngineID::FERMI_TWOD_A: - dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel); + dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel, + EngineTypes::Fermi2D); break; case EngineID::MAXWELL_B: - dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel); + dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel, + EngineTypes::Maxwell3D); break; case EngineID::KEPLER_COMPUTE_B: - dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel); + dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel, + EngineTypes::KeplerCompute); break; case EngineID::MAXWELL_DMA_COPY_A: - dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel); + dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel, + EngineTypes::MaxwellDMA); break; case EngineID::KEPLER_INLINE_TO_MEMORY_B: - dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel); + dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel, + EngineTypes::KeplerMemory); break; default: UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1ba31be88..dd03efecd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -380,6 +380,17 @@ void RasterizerOpenGL::DispatchCompute() { pipeline->SetEngine(kepler_compute, gpu_memory); pipeline->Configure(); const auto& qmd{kepler_compute->launch_description}; + auto indirect_address = kepler_compute->GetIndirectComputeAddress(); + if (indirect_address) { + // DispatchIndirect + static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; + const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; + const auto [buffer, offset] = + buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); + glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer->Handle()); + glDispatchComputeIndirect(static_cast<GLintptr>(offset)); + return; + } glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); ++num_queued_commands; has_written_global_memory |= pipeline->WritesGlobalMemory(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 454bb66a4..c4c30d807 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 89e98425e..590bc1c64 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<vk::DebugUtilsMessenger, vk::DebugReportCallback>; - 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/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 60a6ac651..e15865d16 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<Buffer>& 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()); }); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index fe432dfe1..4f83a88e1 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -665,6 +665,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( std::move(modules), infos); } catch (const Shader::Exception& exception) { + auto hash = key.Hash(); + size_t env_index{0}; + for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + if (key.unique_hashes[index] == 0) { + continue; + } + Shader::Environment& env{*envs[env_index]}; + ++env_index; + + const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; + Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); + env.Dump(hash, key.unique_hashes[index]); + } LOG_ERROR(Render_Vulkan, "{}", exception.what()); return nullptr; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 032f694bc..01e76a82c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -463,6 +463,20 @@ void RasterizerVulkan::DispatchCompute() { pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); const auto& qmd{kepler_compute->launch_description}; + auto indirect_address = kepler_compute->GetIndirectComputeAddress(); + if (indirect_address) { + // DispatchIndirect + static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; + const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; + const auto [buffer, offset] = + buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([indirect_buffer = buffer->Handle(), + indirect_offset = offset](vk::CommandBuffer cmdbuf) { + cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset); + }); + return; + } const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index 67e8065a4..448df2d3a 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<VkDebugReportFlagBitsEXT>(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 a8af7b406..5e940782f 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 bc16145be..180657a75 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; } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 78e5a248f..c3f388d89 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -92,6 +92,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdCopyImage); X(vkCmdCopyImageToBuffer); X(vkCmdDispatch); + X(vkCmdDispatchIndirect); X(vkCmdDraw); X(vkCmdDrawIndexed); X(vkCmdDrawIndirect); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index c226a2a29..049fa8038 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -203,6 +203,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdCopyImage vkCmdCopyImage{}; PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; PFN_vkCmdDispatch vkCmdDispatch{}; + PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect{}; PFN_vkCmdDraw vkCmdDraw{}; PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; PFN_vkCmdDrawIndirect vkCmdDrawIndirect{}; @@ -1209,6 +1210,10 @@ public: dld->vkCmdDispatch(handle, x, y, z); } + void DispatchIndirect(VkBuffer indirect_buffer, VkDeviceSize offset) const noexcept { + dld->vkCmdDispatchIndirect(handle, indirect_buffer, offset); + } + void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, Span<VkBufferMemoryBarrier> buffer_barriers, diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp index 4988fcc83..b457a736a 100644 --- a/src/yuzu/applets/qt_amiibo_settings.cpp +++ b/src/yuzu/applets/qt_amiibo_settings.cpp @@ -160,7 +160,8 @@ void QtAmiiboSettingsDialog::LoadAmiiboData() { } const auto amiibo_name = std::string(register_info.amiibo_name.data()); - const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data()); + const auto owner_name = + Common::UTF16ToUTF8(register_info.mii_char_info.GetNickname().data.data()); const auto creation_date = QDate(register_info.creation_date.year, register_info.creation_date.month, register_info.creation_date.day); diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 8622dc184..fd6bebf0f 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<Settings::RendererBackend>( + combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index()) + .at(api_combobox->currentIndex()) + .first); + }(); + + if (selected_backend == Settings::RendererBackend::Vulkan && + UISettings::values.has_broken_vulkan) { + return Settings::RendererBackend::OpenGL; } - return static_cast<Settings::RendererBackend>( - combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index()) - .at(api_combobox->currentIndex()) - .first); + return selected_backend; } diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index 7721e58f9..d63093985 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp @@ -194,7 +194,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer, return group; } - const auto get_selected = [=]() -> u32 { + const auto get_selected = [=]() -> int { for (const auto& [id, button] : radio_buttons) { if (button->isChecked()) { return id; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b5a02700d..f254c1e1c 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 @@ -588,10 +589,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); }); connect(start_game, &QAction::triggered, [this, path]() { - emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal); + emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal, + AmLaunchType::UserInitiated); }); connect(start_game_global, &QAction::triggered, [this, path]() { - emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global); + emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global, + AmLaunchType::UserInitiated); }); connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); @@ -628,6 +631,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 6c2f75e53..1fcbbf0ba 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -28,6 +28,7 @@ class GameListWorker; class GameListSearchField; class GameListDir; class GMainWindow; +enum class AmLaunchType; enum class StartGameType; namespace FileSys { @@ -103,7 +104,7 @@ public: signals: void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, - StartGameType type); + StartGameType type, AmLaunchType launch_type); void GameChosen(const QString& game_path, const u64 title_id = 0); void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target, @@ -113,6 +114,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 33c9fd0af..97d216638 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,6 +8,7 @@ #include <iostream> #include <memory> #include <thread> +#include "core/loader/nca.h" #ifdef __APPLE__ #include <unistd.h> // for chdir #endif @@ -442,8 +443,13 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>" "here for instructions to fix the issue</a>.")); +#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); renderer_status_button->setChecked(false); } else { @@ -1447,6 +1453,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); @@ -1547,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); } @@ -1698,7 +1707,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 +1720,10 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p system->SetFilesystem(vfs); + if (launch_type == AmLaunchType::UserInitiated) { + system->GetUserChannel().clear(); + } + system->SetAppletFrontendSet({ std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings (UISettings::values.controller_applet_disabled.GetValue() == true) @@ -1849,7 +1863,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 +1907,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; } @@ -2010,8 +2024,16 @@ bool GMainWindow::OnShutdownBegin() { emit EmulationStopping(); + int shutdown_time = 1000; + + if (system->DebuggerEnabled()) { + shutdown_time = 0; + } else if (system->GetExitLocked()) { + shutdown_time = 5000; + } + shutdown_timer.setSingleShot(true); - shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); + shutdown_timer.start(shutdown_time); connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); @@ -2267,40 +2289,62 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); } -static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { - std::size_t out = 0; - - for (const auto& subdir : dir->GetSubdirectories()) { - out += 1 + CalculateRomFSEntrySize(subdir, full); - } - - return out + (full ? dir->GetFiles().size() : 0); -} - -static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src, - const FileSys::VirtualDir& dest, std::size_t block_size, bool full) { +static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog, + const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest, + bool full) { if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) return false; if (dialog.wasCanceled()) return false; + std::vector<u8> buffer(CopyBufferSize); + auto last_timestamp = std::chrono::steady_clock::now(); + + const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file, + const FileSys::VirtualFile& dest_file) { + if (src_file == nullptr || dest_file == nullptr) { + return false; + } + if (!dest_file->Resize(src_file->GetSize())) { + return false; + } + + for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) { + if (dialog.wasCanceled()) { + dest_file->Resize(0); + return false; + } + + using namespace std::literals::chrono_literals; + const auto new_timestamp = std::chrono::steady_clock::now(); + + if ((new_timestamp - last_timestamp) > 33ms) { + last_timestamp = new_timestamp; + dialog.setValue( + static_cast<int>(std::min(read_size, total_size) * 100 / total_size)); + QCoreApplication::processEvents(); + } + + const auto read = src_file->Read(buffer.data(), buffer.size(), i); + dest_file->Write(buffer.data(), read, i); + + read_size += read; + } + + return true; + }; + if (full) { for (const auto& file : src->GetFiles()) { const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); - if (!FileSys::VfsRawCopy(file, out, block_size)) - return false; - dialog.setValue(dialog.value() + 1); - if (dialog.wasCanceled()) + if (!QtRawCopy(file, out)) return false; } } for (const auto& dir : src->GetSubdirectories()) { const auto out = dest->CreateSubdirectory(dir->GetName()); - if (!RomFSRawCopy(dialog, dir, out, block_size, full)) - return false; - dialog.setValue(dialog.value() + 1); - if (dialog.wasCanceled()) + if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full)) return false; } @@ -2573,50 +2617,48 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - FileSys::VirtualFile base_romfs; - if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) { - failed(); - return; - } + FileSys::VirtualFile packed_update_raw{}; + loader->ReadUpdateRaw(packed_update_raw); const auto& installed = system->GetContentProvider(); - const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); - if (!romfs_title_id) { + u64 title_id{}; + u8 raw_type{}; + if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) { failed(); return; } - const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program - : FileSys::ContentRecordType::Data; - const auto base_nca = installed.GetEntry(*romfs_title_id, type); + const auto type = static_cast<FileSys::ContentRecordType>(raw_type); + const auto base_nca = installed.GetEntry(title_id, type); if (!base_nca) { failed(); return; } + const FileSys::NCA update_nca{packed_update_raw, nullptr}; + if (type != FileSys::ContentRecordType::Program || + update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS || + update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) { + packed_update_raw = {}; + } + + const auto base_romfs = base_nca->GetRomFS(); + if (!base_romfs) { + failed(); + return; + } + const auto dump_dir = target == DumpRomFSTarget::Normal ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; - const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); + const auto romfs_dir = fmt::format("{:016X}/romfs", title_id); const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); - FileSys::VirtualFile romfs; - - if (*romfs_title_id == program_id) { - const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), installed}; - romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, nullptr, false); - } else { - romfs = installed.GetEntry(*romfs_title_id, type)->GetRomFS(); - } - - const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); - if (extracted == nullptr) { - failed(); - return; - } + const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; + auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); @@ -2640,11 +2682,16 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } + const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); + if (extracted == nullptr) { + failed(); + return; + } + const auto full = res == selections.constFirst(); - const auto entry_size = CalculateRomFSEntrySize(extracted, full); - // The minimum required space is the size of the extracted RomFS + 1 GiB - const auto minimum_free_space = extracted->GetSize() + 0x40000000; + // The expected required space is the size of the RomFS + 1 GiB + const auto minimum_free_space = romfs->GetSize() + 0x40000000; if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) { QMessageBox::warning(this, tr("RomFS Extraction Failed!"), @@ -2655,12 +2702,15 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, - static_cast<s32>(entry_size), this); + QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + + size_t read_size = 0; - if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) { + if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) { progress.close(); QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), tr("The operation completed successfully.")); @@ -2672,6 +2722,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<int>((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))); @@ -3261,7 +3359,7 @@ void GMainWindow::OnPauseContinueGame() { } void GMainWindow::OnStopGame() { - if (system->GetExitLock() && !ConfirmForceLockedExit()) { + if (system->GetExitLocked() && !ConfirmForceLockedExit()) { return; } @@ -3278,7 +3376,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() { @@ -3758,10 +3857,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); @@ -3905,6 +4008,108 @@ void GMainWindow::OnOpenYuzuFolder() { QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir)))); } +void GMainWindow::OnVerifyInstalledContents() { + // Declare sizes. + size_t total_size = 0; + size_t processed_size = 0; + + // Initialize a progress dialog. + QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); + progress.setWindowModality(Qt::WindowModal); + progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + + // Declare a list of file names which failed to verify. + std::vector<std::string> failed; + + // Declare progress callback. + auto QtProgressCallback = [&](size_t nca_processed, size_t nca_total) { + if (progress.wasCanceled()) { + return false; + } + progress.setValue(static_cast<int>(((processed_size + nca_processed) * 100) / total_size)); + return true; + }; + + // Get content registries. + auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); + auto user_contents = system->GetFileSystemController().GetUserNANDContents(); + + std::vector<FileSys::RegisteredCache*> content_providers; + if (bis_contents) { + content_providers.push_back(bis_contents); + } + if (user_contents) { + content_providers.push_back(user_contents); + } + + // Get associated NCA files. + std::vector<FileSys::VirtualFile> nca_files; + + // Get all installed IDs. + for (auto nca_provider : content_providers) { + const auto entries = nca_provider->ListEntriesFilter(); + + for (const auto& entry : entries) { + auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type); + if (!nca_file) { + continue; + } + + total_size += nca_file->GetSize(); + nca_files.push_back(std::move(nca_file)); + } + } + + // Using the NCA loader, determine if all NCAs are valid. + for (auto& nca_file : nca_files) { + Loader::AppLoader_NCA nca_loader(nca_file); + + auto status = nca_loader.VerifyIntegrity(QtProgressCallback); + if (progress.wasCanceled()) { + break; + } + if (status != Loader::ResultStatus::Success) { + FileSys::NCA nca(nca_file); + const auto title_id = nca.GetTitleId(); + std::string title_name = "unknown"; + + const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id), + FileSys::ContentRecordType::Control); + if (control && control->GetStatus() == Loader::ResultStatus::Success) { + const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), + *provider}; + const auto [nacp, logo] = pm.ParseControlNCA(*control); + if (nacp) { + title_name = nacp->GetApplicationName(); + } + } + + if (title_id > 0) { + failed.push_back( + fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name)); + } else { + failed.push_back(fmt::format("{} (unknown)", nca_file->GetName())); + } + } + + processed_size += nca_file->GetSize(); + } + + progress.close(); + + if (failed.size() > 0) { + auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(failed, "\n"))); + QMessageBox::critical( + this, tr("Integrity verification failed!"), + tr("Verification failed for the following files:\n\n%1").arg(failed_names)); + } else { + QMessageBox::information(this, tr("Integrity verification succeeded!"), + tr("The operation completed successfully.")); + } +} + void GMainWindow::OnAbout() { AboutDialog aboutDialog(this); aboutDialog.exec(); @@ -4350,28 +4555,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() { return mii_nca->GetRomFS().get() != nullptr; } -std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, - u64 program_id) { - const auto dlc_entries = - installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); - std::vector<FileSys::ContentProviderEntry> dlc_match; - dlc_match.reserve(dlc_entries.size()); - std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), - [&program_id, &installed](const FileSys::ContentProviderEntry& entry) { - return FileSys::GetBaseTitleID(entry.title_id) == program_id && - installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; - }); - - std::vector<u64> romfs_tids; - romfs_tids.push_back(program_id); - for (const auto& entry : dlc_match) { - romfs_tids.push_back(entry.title_id); - } - - if (romfs_tids.size() > 1) { - QStringList list{QStringLiteral("Base")}; - for (std::size_t i = 1; i < romfs_tids.size(); ++i) { - list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); +bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, + u64* selected_title_id, u8* selected_content_record_type) { + using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>; + boost::container::flat_map<u64, ContentInfo> available_title_ids; + + const auto RetrieveEntries = [&](FileSys::TitleType title_type, + FileSys::ContentRecordType record_type) { + const auto entries = installed.ListEntriesFilter(title_type, record_type); + for (const auto& entry : entries) { + if (FileSys::GetBaseTitleID(entry.title_id) == program_id && + installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) { + available_title_ids[entry.title_id] = {title_type, record_type}; + } + } + }; + + RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); + RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + + if (available_title_ids.empty()) { + return false; + } + + size_t title_index = 0; + + if (available_title_ids.size() > 1) { + QStringList list; + for (auto& [title_id, content_info] : available_title_ids) { + const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id)); + if (content_info.first == FileSys::TitleType::Application) { + list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id)); + } else { + list.push_back( + QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id)); + } } bool ok; @@ -4379,13 +4597,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv this, tr("Select RomFS Dump Target"), tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); if (!ok) { - return {}; + return false; } - return romfs_tids[list.indexOf(res)]; + title_index = list.indexOf(res); } - return program_id; + const auto selected_info = available_title_ids.nth(title_index); + *selected_title_id = selected_info->first; + *selected_content_record_type = static_cast<u8>(selected_info->second.second); + return true; } bool GMainWindow::ConfirmClose() { @@ -4515,6 +4736,8 @@ void GMainWindow::RequestGameExit() { auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); bool has_signalled = false; + system->SetExitRequested(true); + if (applet_oe != nullptr) { applet_oe->GetMessageQueue()->RequestExit(); has_signalled = true; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1b7055122..cf191f698 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -58,6 +58,11 @@ enum class StartGameType { Global, // Only uses global configuration }; +enum class AmLaunchType { + UserInitiated, + ApplicationInitiated, +}; + namespace Core { enum class SystemResultStatus : u32; class System; @@ -239,9 +244,11 @@ private: void PreventOSSleep(); void AllowOSSleep(); - bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index); + bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index, + AmLaunchType launch_type); void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0, - StartGameType with_config = StartGameType::Normal); + StartGameType with_config = StartGameType::Normal, + AmLaunchType launch_type = AmLaunchType::UserInitiated); void ShutdownGame(); void ShowTelemetryCallout(); @@ -313,6 +320,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); @@ -342,6 +350,7 @@ private slots: void OnConfigurePerGame(); void OnLoadAmiibo(); void OnOpenYuzuFolder(); + void OnVerifyInstalledContents(); void OnAbout(); void OnToggleFilterBar(); void OnToggleStatusBar(); @@ -375,7 +384,8 @@ private: void RemoveAllTransferableShaderCaches(u64 program_id); void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); void RemoveCacheStorage(u64 program_id); - std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); + bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, + u64* selected_title_id, u8* selected_content_record_type); InstallResult InstallNSPXCI(const QString& filename); InstallResult InstallNCA(const QString& filename); void MigrateConfigFiles(); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 013ba0ceb..e54d7d75d 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -148,6 +148,7 @@ <addaction name="action_Configure_Tas"/> </widget> <addaction name="action_Rederive"/> + <addaction name="action_Verify_installed_contents"/> <addaction name="separator"/> <addaction name="action_Capture_Screenshot"/> <addaction name="menuTAS"/> @@ -214,6 +215,11 @@ <string>&Reinitialize keys...</string> </property> </action> + <action name="action_Verify_installed_contents"> + <property name="text"> + <string>Verify installed contents</string> + </property> + </action> <action name="action_About"> <property name="text"> <string>&About yuzu</string> diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index d0433ffc6..55d0938f7 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -265,7 +265,7 @@ int main(int argc, char** argv) { password = match[2]; address = match[3]; if (!match[4].str().empty()) - port = std::stoi(match[4]); + port = static_cast<u16>(std::stoi(match[4])); std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); if (!std::regex_match(nickname, nickname_re)) { std::cout @@ -358,6 +358,7 @@ int main(int argc, char** argv) { system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); + system.GetUserChannel().clear(); const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)}; |